[
  {
    "path": ".editorconfig",
    "content": "[**]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[**.{ts,tsx,json,js,cjs,sass}]\nindent_style = space\nindent_size = tab\ntab_width = 2\n\n[{go.mod,go.sum,*.go}]\nindent_style = tab\nindent_size = 4\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/BUG_REPORT.yml",
    "content": "description: \"Create a bug report\"\nlabels:\n  - bug\nname: \"Bug Report\"\nbody:\n  - attributes:\n      value: \"Thank you for taking the time to fill out this bug report!\\n\"\n    type: markdown\n  - attributes:\n      label: Checklist\n      options:\n        - label:\n            \"I could not find a solution in the existing issues or docs.\"\n          required: true\n        - label:\n            \"I agree to follow this project's [Code of Conduct](https://github.com/teamhanko/hanko/blob/main/CODE_OF_CONDUCT.md).\"\n          required: true\n    id: checklist\n    type: checkboxes\n  - attributes:\n      description: \"A clear and concise description of what the bug is.\"\n      label: \"Describe the bug\"\n      placeholder: \"Tell us what you see!\"\n    id: describe-bug\n    type: textarea\n    validations:\n      required: true\n  - attributes:\n      label: Reproducing the bug\n      description: |\n        Clear, formatted, and easy to follow steps to reproduce the behavior:\n      placeholder: |\n        Steps to reproduce the behavior:\n\n        1. Run `docker run ....`\n        2. Make API Request to with `curl ...`\n        3. Request fails with response: `{\"some\": \"error\"}`\n    id: reproduce-bug\n    type: textarea\n    validations:\n      required: true\n  - attributes:\n      label: Logs\n      description:\n        \"Please copy and paste any relevant log output. This will be\n        automatically formatted into code, so no need for backticks. Please\n        redact any sensitive information!\"\n      render: Shell\n    id: logs\n    type: textarea\n  - attributes:\n      label: Configuration\n      description:\n        \"Please copy and paste any relevant configuration. This will be\n        automatically formatted into code, so no need for backticks. Please\n        redact any sensitive information!\"\n      render: yml\n      placeholder: |\n        server:\n          public:\n            address: :8080\n    id: config\n    type: textarea\n  - attributes:\n      label: Hanko Version\n      description: \"What version of our software are you running? Either version tag (e.g. v1.0.2) or commit short sha (e.g. 26b78e3)\"\n    id: version\n    type: input\n    validations:\n      required: true\n  - attributes:\n      label: OS Hanko Backend\n      description: \"On which operating system is your hanko backend running?\"\n      options:\n        - macOS\n        - Linux\n        - Windows\n        - Other\n    id: operating-system-hanko\n    type: dropdown\n  - attributes:\n      label: OS Version Hanko Backend\n      description: \"Which operating system version are you running the hanko backend on?\"\n    id: operating-system-version-hanko\n    type: textarea\n  - attributes:\n      label: OS\n      description: \"On which operating system are you observing this issue?\"\n      options:\n        - macOS\n        - Linux\n        - Windows\n        - Other\n    id: operating-system-client\n    type: dropdown\n  - attributes:\n      label: OS Version\n      description: \"Which operating system version are you using?\"\n    id: operating-system-version-client\n    type: textarea\n  - attributes:\n      label: Browser Version\n      description: \"Which browser and which version are you using?\"\n    id: browser-version\n    type: textarea\n  - attributes:\n      label: Environment\n      description: \"In which environment are you deploying/running the application(s)?\"\n      options:\n        - Docker\n        - Docker Compose\n        - Binary/Build & Run from Source\n        - Other (e.g. Kubernetes)\n    id: deployment\n    type: dropdown\n  - attributes:\n      description: \"Add any other context (Links, References, Screenshots, Files) pertaining to the problem here.\"\n      label: Additional Context\n    id: additional\n    type: textarea\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml",
    "content": "description:\n  \"Make a feature request\"\nlabels:\n  - enhancement\nname: \"Feature Request\"\nbody:\n  - attributes:\n      value: |\n        Thank you for making a feature request for this project!\n    type: markdown\n  - attributes:\n      label: Checklist\n      options:\n        - label:\n            \"I could not find a solution in the existing issues or docs.\"\n          required: true\n        - label:\n            \"I agree to follow this project's [Code of Conduct](https://github.com/teamhanko/hanko/blob/main/CODE_OF_CONDUCT.md).\"\n          required: true\n    id: checklist\n    type: checkboxes\n  - attributes:\n      label: Description\n      description:\n        \"Is your feature request related to a problem? Please describe.\"\n      placeholder:\n        \"A clear and concise description of what the problem is. Ex. I'm always\n        frustrated when [...]\"\n    id: problem\n    type: textarea\n    validations:\n      required: true\n  - attributes:\n      description: |\n        Describe the solution you'd like to see implemented\n      placeholder: |\n        A clear and concise description of what you want to happen.\n      label: \"Describe your ideal solution\"\n    id: solution\n    type: textarea\n    validations:\n      required: false\n  - attributes:\n      label: Workarounds or alternatives\n      description: \"Describe alternatives you've considered\"\n    id: alternatives\n    type: textarea\n    validations:\n      required: false\n  - attributes:\n      label: Hanko Version\n      description: \"What version of our software are you running?\"\n    id: version\n    type: input\n    validations:\n      required: true\n  - attributes:\n      label: Additional Context\n      description:\n        \"Add any other context or screenshots about the feature request here.\"\n    id: additional\n    type: textarea\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Hanko Discussions\n    url: https://github.com/teamhanko/hanko/discussions/new?category=q-a\n    about:\n      If you have general questions or questions on integrating Hanko, please open up a new discussion in the Q&A section.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!-- Thank you for submitting a pull request for this project! This is a pull request template,\nplease remove any sections that are not applicable. -->\n\n# Description\n\n<!-- Brief description of WHAT you’re doing and WHY. -->\n\n<!-- If applicable, add references to fixed/related issues (e.g. add \"Fixes #<issue>\"/\"Relates to#<issue>\".  -->\n\n# Implementation\n\n<!-- Brief description of HOW you achieved it. Perhaps give a high level description of the program flow. Did you need\nto refactor something? What tradeoffs did you take? Are there any caveats/downsides to your solution? Are there things\nin here which you’d particularly like people to pay close attention to? -->\n\n# Tests\n\n<!-- Describe how to verify your changes. Provide instructions for the purpose of reproducibility. List any relevant\ndetails for your test configuration. -->\n\n# Todos\n\n<!-- Are there any other outstanding issues or tasks that must be solved before this change can be possibly merged? -->\n\n# Additional context\n\n<!-- Add any other relevant context pertaining to the proposed change. For example, if the change can be visualized,\ninclude screenshots or diagrams to indicate the state before and after the change. -->\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/backend\"\n    schedule:\n      interval: \"daily\"\n\n  - package-ecosystem: \"npm\"\n    directory: \"/frontend\"\n    schedule:\n      interval: \"daily\"\n    # Always increase the version requirement\n    # to match the new version.\n    versioning-strategy: increase\n\n  - package-ecosystem: \"github-actions\"\n    # Workflow files stored in the default location of `.github/workflows`.\n    # (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: \"/\"`.)\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/build-frontend.yml",
    "content": "name: build-frontend\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Setup Node\n        uses: actions/setup-node@v6\n        with:\n          node-version: v24.11.1\n\n      - name: Install dependencies\n        working-directory: ./frontend\n        run: npm ci\n\n      - name: Build\n        working-directory: ./frontend\n        run: npm run build\n\n      - name: Run tests\n        working-directory: ./frontend\n        run: npm test\n"
  },
  {
    "path": ".github/workflows/cli-publish.yml",
    "content": "name: CLI\n\non:\n  release:\n    types: [published]\n\npermissions:\n  contents: write\n\njobs:\n  goreleaser:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: actions/setup-go@v6\n        with:\n          go-version: stable\n      - uses: goreleaser/goreleaser-action@v7\n        with:\n          distribution: goreleaser\n          version: latest\n          args: release --clean --snapshot\n          workdir: ./backend\n        env:\n          GORELEASER_CURRENT_TAG: 'v0.0.0' # can be anything since we're using --snapshot\n      - run: |\n          mkdir -p dist/artifacts\n          cp dist/*.{tar.gz,zip} dist/artifacts\n        working-directory: ./backend\n      - name: Upload assets\n        uses: softprops/action-gh-release@v2\n        with:\n          files: ./backend/dist/artifacts/*\n          fail_on_unmatched_files: true\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ main ]\n  schedule:\n    - cron: '22 12 * * 3'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'go', 'javascript' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]\n        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v6\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        \n        # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs\n        # queries: security-extended,security-and-quality\n\n        \n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v4\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n\n    #   If the Autobuild fails above, remove it and uncomment the following three lines. \n    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.\n\n    # - run: |\n    #   echo \"Run, Build Application using script\"\n    #   ./location_of_script_within_repo/buildscript.sh\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/docker-publish.yml",
    "content": "name: Docker\n\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\non:\n  push:\n    branches: [ main ]\n    # Publish semver tags as releases.\n    tags: [ 'backend/v*.*.*' ]\n  pull_request:\n    branches: [ main ]\n\nenv:\n  # Use docker.io for Docker Hub if empty\n  REGISTRY: ghcr.io\n  # github.repository as <account>/<repo>\n  IMAGE_NAME: ${{ github.repository }}\n\n\njobs:\n  build-and-push:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - context: ./backend\n            image: ghcr.io/teamhanko/hanko\n          - context: ./quickstart\n            image: ghcr.io/teamhanko/hanko/quickstart\n          - context: ./frontend\n            image: ghcr.io/teamhanko/hanko/elements\n    permissions:\n      contents: read\n      packages: write\n      # This is used to complete the identity challenge\n      # with sigstore/fulcio when running outside of PRs.\n      id-token: write\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v4\n\n      - name: Setup Docker buildx\n        uses: docker/setup-buildx-action@v4\n\n      # Login against a Docker registry except on PR\n      # https://github.com/docker/login-action\n      - name: Log into registry ${{ env.REGISTRY }}\n        if: github.event_name != 'pull_request'\n        uses: docker/login-action@v4\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      # Extract metadata (tags, labels) for Docker\n      # https://github.com/docker/metadata-action\n      - name: Extract Docker metadata\n        id: meta\n        uses: docker/metadata-action@v6\n        with:\n          images: ${{ matrix.image }}\n          tags: |\n            type=schedule\n            type=ref,event=branch,suffix=-{{sha}}-{{date 'YYYY-MM-DD.HHmmss'}}\n            type=match,pattern=backend/(v\\d+.\\d+.\\d+),group=1,event=tag\n            type=ref,event=pr\n\n      # Build and push Docker image with Buildx (don't push on PR)\n      # https://github.com/docker/build-push-action\n      - name: Build and push Docker image\n        id: build-and-push\n        uses: docker/build-push-action@v7\n        with:\n          platforms: linux/amd64,linux/arm64\n          context: ${{ matrix.context }}\n          file: ${{ matrix.context }}/Dockerfile\n          push: ${{ github.event_name != 'pull_request' }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n"
  },
  {
    "path": ".github/workflows/e2e.yml",
    "content": "name: E2E Test\n\non:\n  workflow_dispatch:\n\njobs:\n  passwordless:\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Start containers\n        working-directory: ./deploy/docker-compose\n        run: docker compose -f \"quickstart.e2e.yaml\" up -d --build\n\n      - name: Install dependencies\n        working-directory: ./e2e\n        run: |\n          npm install\n          npx playwright install chromium\n\n      - name: Run tests\n        working-directory: ./e2e\n        run: npm run 'test:nopw'\n\n      - name: Stop containers\n        if: always()\n        working-directory: ./deploy/docker-compose\n        run: docker compose -f \"quickstart.e2e.yaml\" down\n  passwords:\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Start containers\n        working-directory: ./deploy/docker-compose\n        run: docker compose -f \"quickstart.e2e.yaml\" up -d --build\n        env:\n          PASSWORD_ENABLED: true\n\n      - name: Install dependencies\n        working-directory: ./e2e\n        run: |\n          npm install\n          npx playwright install chromium\n\n      - name: Run tests\n        working-directory: ./e2e\n        run: npm run 'test:pw'\n\n      - name: Stop containers\n        if: always()\n        working-directory: ./deploy/docker-compose\n        run: docker compose -f \"quickstart.e2e.yaml\" down\n  nosignup:\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Copy config\n        working-directory: ./deploy/docker-compose\n        run: cp config-disable-signup.yaml config.yaml\n\n      - name: Start containers\n        working-directory: ./deploy/docker-compose\n        run: docker compose -f \"quickstart.e2e.yaml\" up -d --build\n\n      - name: Install dependencies\n        working-directory: ./e2e\n        run: |\n          npm install\n          npx playwright install chromium\n\n      - name: Run tests\n        working-directory: ./e2e\n        run: npm run 'test:nosignup'\n\n      - name: Stop containers\n        if: always()\n        working-directory: ./deploy/docker-compose\n        run: docker compose -f \"quickstart.e2e.yaml\" down\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n\n    - name: Set up Go\n      uses: actions/setup-go@v6\n      with:\n        go-version: '1.26'\n\n    - name: Build\n      working-directory: ./backend\n      run: |\n        go generate ./...\n        go build -v ./...\n\n    - name: Test\n      working-directory: ./backend\n      run: go test -v ./...\n"
  },
  {
    "path": ".github/workflows/release-frontend-sdk.yml",
    "content": "name: Release @teamhanko/frontend-sdk\n\non:\n  push:\n    tags:\n      - '@teamhanko/frontend-sdk@*'\n\ndefaults:\n  run:\n    working-directory: frontend/frontend-sdk\n\npermissions:\n  contents: write  # Added to allow pushing to gh-pages\n  id-token: write  # Required for OIDC\n\njobs:\n  check-matching-versions:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: get-npm-version\n        id: package-version\n        uses: martinbeentjes/npm-get-version-action@main\n        with:\n          path: frontend/frontend-sdk\n      - run: |\n          version=$(echo $GITHUB_REF | cut -f3 -d'@')\n          echo \"git_tag_version=$version\" >> $GITHUB_OUTPUT\n        id: tag-version\n      - run: echo ${{ steps.tag-version.outputs.git_tag_version }}\n      - name: Version correctly set check\n        if: steps.package-version.outputs.current-version != steps.tag-version.outputs.git_tag_version\n        uses: actions/github-script@v8\n        with:\n          script: |\n            core.setFailed('version in package.json is not equal to git tag version!')\n\n  build-and-publish:\n    needs: check-matching-versions\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24.11.1\n          registry-url: https://registry.npmjs.org/\n      - run: npm ci\n      - run: npm run build\n      - run: npm test\n      - name: publish frontend-sdk\n        run: npm publish\n\n  generate-docs:\n    needs: build-and-publish\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n      - name: Setup node\n        uses: actions/setup-node@v6\n        with:\n          node-version: 24.11.1\n      - name: Install dependencies\n        run: npm ci\n      - name: Generate docs\n        run: npm run docs\n      - name: Deploy to gh-pages branch\n        uses: peaceiris/actions-gh-pages@v4\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: frontend/frontend-sdk/.generated/docs\n          destination_dir: jsdoc/hanko-frontend-sdk\n"
  },
  {
    "path": ".github/workflows/release-hanko-elements.yml",
    "content": "name: Release @teamhanko/hanko-elements\n\non:\n  push:\n    tags:\n      - '@teamhanko/hanko-elements@*'\n\ndefaults:\n  run:\n    working-directory: frontend/elements\n\npermissions:\n  id-token: write  # Required for OIDC\n\njobs:\n  check-matching-versions:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: get-npm-version\n        id: package-version\n        uses: martinbeentjes/npm-get-version-action@main\n        with:\n          path: frontend/elements\n      - run: |\n          version=$(echo $GITHUB_REF | cut -f3 -d'@')\n          echo \"git_tag_version=$version\" >> $GITHUB_OUTPUT\n        id: tag-version\n      - run: echo ${{ steps.tag-version.outputs.git_tag_version }}\n      - name: Version correctly set check\n        if: steps.package-version.outputs.current-version != steps.tag-version.outputs.git_tag_version\n        uses: actions/github-script@v8\n        with:\n          script: |\n            core.setFailed('version in package.json is not equal to git tag version!')\n\n  build-and-publish:\n    needs: check-matching-versions\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24.11.1\n          registry-url: https://registry.npmjs.org/\n      - name: build-frontend-sdk\n        run: |\n          npm ci\n          npm run build\n        working-directory: frontend/frontend-sdk\n      - name: build-elements\n        run: |\n          npm ci\n          npm run build\n      - name: publish-elements\n        run: npm publish\n"
  },
  {
    "path": ".github/workflows/schema-generate-config.yml",
    "content": "name: Generate config JSON schema\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - 'backend/config/*.go'\n      - '!backend/config/config_default.go'\n      - '!backend/config/**test.go'\n\njobs:\n  config:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '1.24'\n\n      - name: Checkout backend\n        uses: actions/checkout@v6\n        with:\n          path: hanko\n\n      - name: Generate config JSON schema\n        working-directory: ./hanko/backend\n        run: |\n          go generate ./...\n          go run main.go schema generate config\n\n      - name: Create pull request\n        working-directory: ./hanko\n        run: |\n          git config --local user.email \"action@github.com\"\n          git config --local user.name \"GitHub Action\"\n          git add .\n          if ! git diff-index --quiet HEAD; then\n            git checkout -b \"chore-autogenerate-config-json-schema-${{ github.run_id }}\"\n            git commit -m \"chore: autogenerate config JSON schema\"\n            git push origin HEAD\n            gh pr create \\\n              -B main \\\n              -H \"chore-autogenerate-config-json-schema-${{ github.run_id }}\" \\\n              -t \"chore: autogenerate config JSON schema\" \\\n              -b '# Description\n\n             Autogenerate config JSON schema. Created by Github Action.\n\n             # Additional context\n\n             Ref: `${{ github.ref }}`\n             Workflow name: `${{ github.workflow }}`\n             Job ID: `${{ github.job }}`\n             Run ID: `${{ github.run_id }}`'\n          else\n            echo \"No changes detected, skipping pull request creation.\"\n          fi\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n"
  },
  {
    "path": ".github/workflows/schema-generate-import.yml",
    "content": "name: Generate import JSON schema\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - backend/cmd/user/format.go\n\njobs:\n  import:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '1.24'\n\n      - name: Checkout backend\n        uses: actions/checkout@v6\n        with:\n          path: hanko\n\n      - name: Generate import JSON schema\n        working-directory: ./hanko/backend\n        run: |\n          go generate ./...\n          go run main.go schema generate import\n\n      - name: Create pull request\n        working-directory: ./hanko\n        run: |\n          git config --local user.email \"action@github.com\"\n          git config --local user.name \"GitHub Action\"\n          git add .\n          if ! git diff-index --quiet HEAD; then\n            git checkout -b \"chore-autogenerate-import-json-schema-${{ github.run_id }}\"\n            git commit -m \"chore: autogenerate import JSON schema\"\n            git push origin HEAD\n            gh pr create \\\n              -B main \\\n              -H \"chore-autogenerate-import-json-schema-${{ github.run_id }}\" \\\n              -t \"chore: autogenerate import JSON schema\" \\\n              -b '# Description\n\n             Autogenerate import JSON schema. Created by Github Action.\n\n             # Additional context\n\n             Ref: `${{ github.ref }}`\n             Workflow name: `${{ github.workflow }}`\n             Job ID: `${{ github.job }}`\n             Run ID: `${{ github.run_id }}`'\n          else\n            echo \"No changes detected, skipping pull request creation.\"\n          fi\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/schema-markdown-config.yml",
    "content": "name: Generate config reference markdown\n\non:\n  push:\n    tags:\n      - 'backend/*'\n  workflow_dispatch:\n\njobs:\n  config:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '1.24'\n\n      - uses: actions/setup-node@v6\n        with:\n          node-version: '20.16.0'\n          registry-url: https://registry.npmjs.org/\n\n      - name: Checkout backend\n        uses: actions/checkout@v6\n        with:\n          path: hanko\n\n      - name: Checkout backend wiki\n        uses: actions/checkout@v6\n        with:\n          repository: ${{github.repository}}.wiki\n          path: wiki\n\n      - name: Generate config markdown\n        working-directory: ./hanko/backend\n        run: |\n          go generate ./...\n          go run main.go schema markdown config\n\n      - name: Strip links of .md file endings\n        working-directory: ./hanko/backend\n        run: |\n          find ./.generated/docs/config -type f -name \"*.md\" -exec sed -i \"s/\\.md//g\" \"{}\" \\;\n\n      - name: Prepend version information\n        working-directory: ./hanko/backend\n        run: |\n          version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')\n          find ./.generated/docs/config -type f -name '*.md' -exec sed -i \"1i\\\\\n          ## Version\\\\\n          \\\\\n          \\`$version\\`\\\\\n          \\\\\n          \" {} \\;\n\n      - name: Copy generated files\n        working-directory: ./hanko/backend\n        run: |\n          mkdir -p $GITHUB_WORKSPACE/wiki/reference/config\n          rm $GITHUB_WORKSPACE/wiki/reference/config/*.md 2>/dev/null || true\n          cp .generated/docs/config/*.md $GITHUB_WORKSPACE/wiki/reference/config\n\n      - name: Commit and push to wiki\n        working-directory: ./wiki\n        run: |\n          git config --local user.email \"action@github.com\"\n          git config --local user.name \"GitHub Action\"\n          git add .\n          if ! git diff-index --quiet HEAD; then\n            version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')\n            git commit -m \"chore: autogenerate config reference for $version\"\n            git pull origin master --rebase\n            git push origin HEAD\n          else\n            echo \"No changes detected, skipping commit and push.\"\n          fi\n"
  },
  {
    "path": ".github/workflows/schema-markdown-import.yml",
    "content": "name: Generate user import reference markdown\n\non:\n  push:\n    tags:\n      - 'backend/*'\n  workflow_dispatch:\n\njobs:\n  import:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '1.24'\n\n      - uses: actions/setup-node@v6\n        with:\n          node-version: '20.16.0'\n          registry-url: https://registry.npmjs.org/\n\n      - name: Checkout backend\n        uses: actions/checkout@v6\n        with:\n          path: hanko\n\n      - name: Checkout backend wiki\n        uses: actions/checkout@v6\n        with:\n          repository: ${{github.repository}}.wiki\n          path: wiki\n\n      - name: Generate import markdown\n        working-directory: ./hanko/backend\n        run: |\n          go generate ./...\n          go run main.go schema markdown import\n\n      - name: Strip links of .md file endings\n        working-directory: ./hanko/backend\n        run: |\n          find ./.generated/docs/import -type f -name \"*.md\" -exec sed -i \"s/\\.md//g\" \"{}\" \\;\n\n      - name: Prepend version information\n        working-directory: ./hanko/backend\n        run: |\n          version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')\n          find ./.generated/docs/import -type f -name '*.md' -exec sed -i \"1i\\\\\n          ## Version\\\\\n          \\\\\n          \\`$version\\`\\\\\n          \\\\\n          \" {} \\;\n\n      - name: Copy generated files\n        working-directory: ./hanko/backend\n        run: |\n          mkdir -p $GITHUB_WORKSPACE/wiki/reference/import\n          rm $GITHUB_WORKSPACE/wiki/reference/import/*.md 2>/dev/null || true\n          cp .generated/docs/import/*.md $GITHUB_WORKSPACE/wiki/reference/import\n\n      - name: Delay check\n        run: |\n          OWNER=${{ github.repository_owner }}\n          NAME=\"$(cut -f2 -d'/' <<< ${{ github.repository }})\"\n          HEAD_SHA=\"${{ github.sha }}\"\n          STATUS=\"in_progress\"\n          BASE=\"/repos/${OWNER}/${NAME}/actions/workflows/schema-markdown-config.yml/runs\"\n          REQUEST_URL=\"${BASE}?head_sha=${HEAD_SHA}&status=${STATUS}\"\n          SHOULD_DELAY=$(gh api \"$REQUEST_URL\" --jq '.total_count > 0')\n          if [ \"$SHOULD_DELAY\" = \"true\" ]; then\n            echo \"Config markdown generation job in progress, delaying to avoid conflicts\"\n            sleep 60s\n          else\n            echo \"No config markdown generation job in progress, continuing\"\n          fi\n        shell: bash\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Commit and push to wiki\n        working-directory: ./wiki\n        run: |\n          git config --local user.email \"action@github.com\"\n          git config --local user.name \"GitHub Action\"\n          git add .\n          if ! git diff-index --quiet HEAD; then\n            version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')\n            git commit -m \"chore: autogenerate import reference for $version\"\n            git pull origin master --rebase\n            git push origin HEAD\n          else\n            echo \"No changes detected, skipping commit and push.\"\n          fi\n"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n#env files\n*.env\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Generated files\n.generated\n\n# MacOS\n.DS_Store\n\n## JetBrains\n.idea\n\nhanko\n\n.turbo\n\n# Dependency directories (remove the comment below to include it)\nnode_modules\ndist\ne2e/test-results/\ne2e/playwright-report/\ne2e/playwright/.cache/\n/backend/build_info/version.txt\n/backend/dist\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\ncoc@hanko.io.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Hanko\n\nThank you for considering contributing to Hanko! Following are the guidelines we would like you to follow:\n\n- [Code of Conduct](#code-of-conduct)\n- [Communication](#communication)\n- [Reporting Issues](#reporting-issues)\n  - [Security](#security)\n  - [Bugs](#bugs)\n- [Feature Requests](#feature-requests)\n- [Submitting Code](#submitting-code)\n- [Commit Message Guidelines](#commit-message-guidelines)\n- [Style Guidelines](#style-guidelines)\n\n## Code of Conduct\n\nWe expect all contributors to adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md).\n\n## Communication\n\nIf you have any questions, want to discuss bugs or feature requests, or just want talk to other Hanko users you are welcome\nto join our [Slack](https://hanko.io/community) community or use the [Hanko Discussions](https://github.com/teamhanko/hanko/discussions)\n(especially useful for long term discussion or larger questions).\n\n## Reporting issues\n\nReporting issues requires a [GitHub](https://github.com/) account. Please do not use the issue\ntracker for general support questions but use the above mentioned [communication](#communication) channels.\n\n### Security\n\nPursuant to our [security policy](./SECURITY.md), any security vulnerabilities should be reported directly to\n`security@hanko.io` instead of using the issue tracker.\n\n### Bugs\n\nBugs are tracked as [GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues).\nWhen reporting a bug choose \"Bug Report\" when [creating](https://github.com/teamhanko/hanko/issues/new/choose) a new\nissue. Doing so will present you with a template form to fill out. Be as detailed as possible. Good\nbug reports are vital, so thank you for taking the time!\n\nBefore reporting a bug:\n\n1. Take a look at the [existing issues](https://github.com/teamhanko/hanko/issues?q=is%3Aissue+label%3Abug) and make\n   sure the issue hasn't already been reported. If you find a similar bug and the issue is still open, consider adding\n   a comment providing any new information that you might be able to report.\n2. Make sure the issue hasn't been fixed already. Try to reproduce it using the latest `main` branch in the repository if\n   you were not working with the latest version.\n3. Make sure the bug is really a bug. If you need general support or are unsure whether some behaviour represents a bug\n   please do not file a bug ticket but reach out through the above mentioned [communication](#communication) channels.\n4. Gather as much information about the bug as you can. Logs, screenshots/screen captures, steps to reproduce the bug\n   can be vital for a useful bug report.\n\nIf you already have suggestions on how to fix the bug, do not hesitate to include them in the bug description.\n\n## Feature requests\n\nJust like bugs, feature requests are tracked as [GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues).\nWhen suggesting an enhancement choose \"Feature Request\" when [creating](https://github.com/teamhanko/hanko/issues/new/choose) a new\nissue and fill out the template form.\n\nBefore making a feature request:\n\n1. Take a look at the [existing issues](https://github.com/teamhanko/hanko/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) and make\n   sure the issue hasn't already been reported.\n2. Make sure the issue hasn't been implemented already. Always try using the latest `main` branch in the repository if\n   to confirm the feature is not already in place.\n\nWhen filling out the template form, be sure to be as detailed as possible. Describe the current behavior and explain\nwhich behavior you expect to see instead. Explain why this enhancement would be useful to Hanko users.\n\n## Submitting Code\n\nContributing code requires a [GitHub](https://github.com/) account. All contributions are made via\n[pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).\nPull requests should target the `main` branch.\n\nTo submit your code:\n\n1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo#forking-a-repository) the [repository](https://github.com/teamhanko/hanko).\n2. [Clone](https://docs.github.com/en/get-started/quickstart/fork-a-repo#cloning-your-forked-repository) the forked repository.\n3. [Configure remotes](https://docs.github.com/en/get-started/quickstart/fork-a-repo#configuring-git-to-sync-your-fork-with-the-original-repository).\n4. Create a new [branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches)\n   off of the `main` branch.\n   ```\n   git checkout -b <new-branch-name>\n   ```\n5. Make your changes. Make sure to follow the [Style Guidelines](#style-guidelines). Commit your changes.\n   ```\n   git add -A\n   git commit\n   ```\n   Commit messages should follow the [Commit Message Guidelines](#commit-message-guidelines).\n6. Make sure to update, or add to any tests where appropriate. Try to run tests locally first (`go test ./...` for the\n   `backend`, see the [README](./e2e/README.md) for the `e2e`tests on how to run them).\n7. Push your feature branch up to your fork:\n   ```\n   git push origin <feature-branch-name>\n   ```\n8. [Create a pull request from your fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork).\n9. Submit the pull request by filling out the [pull request template](./.github/PULL_REQUEST_TEMPLATE.md)\n    (note: the template should be displayed automatically once you open a pull request; take account of the comments in\n    the displayed template).\n10. If a pull request is not ready to be reviewed it should be marked as a \"Draft\".\n\n\nWhen pull requests fail test checks, authors are expected to update\ntheir pull requests to address the failures until the tests pass. If you have trouble or questions on how to add to\nexisting tests, reach out through our [communication](#communication) channels.\n\n# Commit Message Guidelines\n\nCommit messages should adhere to the\n[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.\nThe commit message should be structured as follows:\n\n```\n<type>(<optional scope>): <description>\n\n<optional body>\n\n<optional footer(s)>\n```\n\nThe commit message headline should have the following structure:\n```\n<type>(<optional scope>): <description>\n   │            │               │\n   │            │               └─⫸ Summary in present tense. Not capitalized. No period at the end.\n   │            │\n   │            └─⫸ Commit Scope: optional\n   │\n   └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test|chore\n```\nThe `<type>` should be one of the following:\n* **build**: Changes that affect the build system\n* **ci**: Changes that affect the CI workflows (e.g. changes to `.github` CI configuration files)\n* **docs**: Documentation only changes (this includes both content in the `docs` as well as changes to readmes)\n* **feat**: A new feature\n* **fix**: A bug fix\n* **perf**: A code change that improves performance\n* **refactor**: A code change that neither fixes a bug nor adds a feature\n* **test**: Adding missing tests or correcting existing tests\n* **chore**: Anything that cannot be categorized properly using the above prefixes (e.g. increasing versions)\n\nThe `<scope>` is optional. If present, it should be the name of the (npm) package or directory affected by the changes of\nthe commit.\n\n# Style Guidelines\n\n## Go\n\nGo files should be [formatted](https://go.dev/blog/gofmt) according to gofmt's rules.\n\n```\n# single file\ngo fmt path/to/changed/file.go\n\n# all files, e.g. in 'backend' directory\ngo fmt ./...\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "  Portions of this software are licensed as follows:\n\n  All content that resides under the \"elements\" directory\n(https://github.com/teamhanko/hanko/tree/main/frontend/elements) of this repository is\nlicensed under the license defined in \"elements/LICENSE\".\n\n  All content that resides under the \"frontend-sdk\" directory\n(https://github.com/teamhanko/hanko/tree/main/frontend/frontend-sdk) of this repository is\nlicensed under the license defined in \"frontend-sdk/LICENSE\".\n\n  All third party components incorporated into Hanko software are licensed\nunder the original license provided by the owner of the applicable component.\n\n  Content outside of the above mentioned directories or restrictions above is\navailable under the \"AGPLv3\" license as defined below.\n\n--\n\n                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img width=\"300\" src=\"https://user-images.githubusercontent.com/20115649/176922807-fb92327a-15d5-4568-a4e7-78093cea045e.svg?sanitize=true#gh-light-mode-only\">\n  <img width=\"300\" src=\"https://user-images.githubusercontent.com/20115649/176922819-61dfb644-529f-4f81-a577-7daa47185300.svg?sanitize=true#gh-dark-mode-only\">\n</p>\n\n---\n[![Test Status](https://github.com/teamhanko/hanko/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/teamhanko/hanko/actions/workflows/codeql-analysis.yml)\n[![Build Status](https://github.com/teamhanko/hanko/workflows/Go/badge.svg)](https://github.com/teamhanko/hanko/actions/workflows/go.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/teamhanko/hanko)](https://goreportcard.com/report/github.com/teamhanko/hanko)\n[![GoDoc](https://godoc.org/github.com/teamhanko/hanko?status.svg)](https://godoc.org/github.com/teamhanko/hanko)\n[![npm (scoped)](https://img.shields.io/npm/v/@teamhanko/hanko-elements?label=hanko-elements)](https://www.npmjs.com/package/@teamhanko/hanko-elements)\n[![npm (scoped)](https://img.shields.io/npm/v/@teamhanko/hanko-frontend-sdk?label=hanko-frontend-sdk)](https://www.npmjs.com/package/@teamhanko/hanko-frontend-sdk)\n\n# About Hanko\nHanko is an open source authentication and user management solution that is easy to integrate, framework-agnostic, and built on privacy-first principles like data minimalism and phishing resistance.\n\n- Supports all modern authentication methods: passwords, MFA, passkeys, social logins, and SAML SSO\n- Flexible configuration options, including passkey-only, OAuth-only, and user-deletable passwords\n- Easy integration with **Hanko Elements** web components\n- A robust API that handles all authentication and onboarding flow states, enabling fast, reliable custom frontend implementations\n- API-first, lightweight, cloud-native\n\nAvailable for self-hosting and as a fully managed service on [Hanko Cloud](https://www.hanko.io).\n\n# Features\nTo follow the development of this project, watch our releases, leave a star, sign up to our [Product News](https://www.hanko.io/updates) or join our [Discord Community](https://www.hanko.io/community). Here's a brief overview of Hanko's current and upcoming features:\n\n| Status | Feature |\n|:------:| :--- |\n|✅| Email / username identifiers |\n|✅| Passwords, passcodes, passkeys |\n|✅| Hanko Elements web components |\n|✅| OAuth SSO (Sign in with Apple/Google/GitHub and more) |\n|✅| i18n & custom translations |\n|✅| SAML Enterprise SSO |\n|✅| Webhooks |\n|✅| Server-side sessions & remote session revocation |\n|✅| MFA (TOTP, security keys) |\n|✅| Custom OIDC/OAuth connections |\n|✅| JS SDK |\n|⚙️| Organizations, Roles, Permissions |\n| | `<hanko-menu>` web component |\n| | iOS, Android, React Native, Flutter SDKs |\n\nVisit our [Roadmap](https://www.hanko.io/roadmap) for more information on upcoming features.\n\n# Contact us\nSchedule a demo with the team. Learn how you can built state-of-the-art authentication for your apps effortlessly with Hanko.\n\n<a target=\"_blank\" href=\"https://cal.com/team/hanko/demo\"><img alt=\"Book us with Cal.com\"  src=\"https://cal.com/book-with-cal-light.svg\" /></a>\n\n# Architecture\nThe main building blocks of the Hanko project are\n- [backend](/backend/README.md) - Scalable, robust, and lightweight authentication API for passwords, passkeys, email passcodes, OAuth SSO, user and session management, and JWT issuing\n- [hanko-elements](/frontend/elements/README.md) - Web components made for the Hanko API that provide onboarding, login, and user profile functionality and are customizable with CSS\n- [hanko-frontend-sdk](/frontend/frontend-sdk/README.md) - A client package for using the Hanko API\n\nThe remainder of the repository consists of:\n- [quickstart](/quickstart) - A quickstart example app showing off Hanko's login experience and acting as a reference implementation\n- [examples](frontend/examples) - Example implementations for a number of frameworks\n- docs - The Hanko documentation ([docs.hanko.io](https://docs.hanko.io)) -> Moved to its own repo here: https://github.com/teamhanko/docs\n\n# Getting started\n1. Try our hosted [live example](https://example.hanko.io) and our companion page [passkeys.io](https://www.passkeys.io) or use the [quickstart app](/quickstart/README.md) to get a feel for the user experience provided by an application that leverages the Hanko backend API and our custom web component\n2. To run the project locally, there are two options available:\n   - Bare metal:\n      - Head over to the [backend](/backend/README.md) section to learn how to get it up and running for your own project. Use [Hanko Cloud](https://cloud.hanko.io) for a hosted backend.\n   - Docker:\n     -  If you prefer to use [Docker](https://www.docker.com/) to run the project locally, please visit the [Run the quickstart](./quickstart/README.md#run-the-quickstart) for information on how to run the project. This will create everything, including frontend and backend components. \n        -  If you wish to keep only the backend components, you can modify the [quickstart.yaml](./deploy/docker-compose/quickstart.yaml) to remove the unnecessary services. To make changes to the configuration to meet your needs, modify [config.yaml](./deploy/docker-compose/config.yaml).\n3. Then, integrate [hanko-elements](/frontend/elements/README.md) – we provide [example applications](frontend/examples/README.md) and [guides](https://docs.hanko.io/guides/frontend) for your favourite frontend framework in the official documentation\n\nIf you want to use the Hanko backend API but prefer to build your own UI, you can still make use of the [hanko-frontend-sdk](/frontend/frontend-sdk/README.md). It forms the basis of our web components, and the client it provides handles communication with the [Hanko backend API](https://docs.hanko.io/api-reference/introduction) and saves you the time of rolling your own.\n\n# Community\n## Questions, bugs, ideas\nIf you have any questions or issues, please check this project's [Q&A section in discussions](https://github.com/teamhanko/hanko/discussions/categories/q-a) and the [open issues](https://github.com/teamhanko/hanko/issues). Feel free to comment on existing issues or create a new issue if you encounter any bugs or have a feature request. For yet unanswered questions, feedback, or new ideas, please open a new discussion.\n\n## Discord community & X\nWe invite you to join our growing [Discord Community](https://www.hanko.io/community) if you want to get the latest updates on passkeys, WebAuthn, and this project or if you just want to chat with us. You can also [follow us on X](https://x.com/hanko_io).\n\n# Licenses\n[hanko-elements](frontend/elements) and [hanko-frontend-sdk](frontend/frontend-sdk) are licensed under the [MIT License](frontend/elements/LICENSE). Everything else in this repository, including [hanko backend](backend), is licensed under the [AGPL-3.0](/LICENSE). Non-Copyleft commercial licensing is available on request.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nIf you discover a security vulnerability, we appreciate any information about it so that we can fix the problem as soon as possible.\n\nPlease email us at security@hanko.io.\n\n- We will evaluate the report and respond within 3 business days\n- We will keep your report confidential and will not share your personal information with any third party without your consent\n- We will keep you informed of the progress until the problem is resolved\n"
  },
  {
    "path": "backend/.goreleaser.yaml",
    "content": "before:\n  hooks:\n    - go mod tidy\n    - go generate ./...\nbuilds:\n  - env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n      - windows\n      - darwin\n\narchives:\n  - format: tar.gz\n    name_template: >-\n      {{ .ProjectName }}_\n      {{- title .Os }}_\n      {{- if eq .Arch \"amd64\" }}x86_64\n      {{- else if eq .Arch \"386\" }}i386\n      {{- else }}{{ .Arch }}{{ end }}\n      {{- if .Arm }}v{{ .Arm }}{{ end }}\n    format_overrides:\n    - goos: windows\n      format: zip\n"
  },
  {
    "path": "backend/Dockerfile",
    "content": "# Build the hanko binary\nFROM --platform=$BUILDPLATFORM golang:1.26 AS builder\n\nARG TARGETARCH\n\nWORKDIR /workspace\nCOPY go.mod go.mod\nCOPY go.sum go.sum\nRUN go mod download\n\n# Copy the go source\nCOPY main.go main.go\nCOPY cmd cmd/\nCOPY config config/\nCOPY persistence persistence/\nCOPY server server/\nCOPY handler handler/\nCOPY crypto crypto/\nCOPY dto dto/\nCOPY ee/saml ee/saml/\nCOPY session session/\nCOPY mail mail/\nCOPY audit_log audit_log/\nCOPY pagination pagination/\nCOPY rate_limiter rate_limiter/\nCOPY thirdparty thirdparty/\nCOPY build_info build_info/\nCOPY middleware middleware/\nCOPY template template/\nCOPY utils utils/\nCOPY mapper mapper/\nCOPY webhooks webhooks/\nCOPY flow_api flow_api/\nCOPY flowpilot flowpilot/\n\n# Build\nRUN go generate ./...\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=\"$TARGETARCH\" go build -a -o hanko main.go\n\n# Use distroless as minimal base image to package hanko binary\n# See https://github.com/GoogleContainerTools/distroless for details\nFROM gcr.io/distroless/static:nonroot\nWORKDIR /\nCOPY --from=builder /workspace/hanko .\nUSER 65532:65532\n\nENTRYPOINT [\"/hanko\"]\n"
  },
  {
    "path": "backend/Dockerfile.debug",
    "content": "# Build the hanko binary\nFROM golang:1.26 AS builder\nWORKDIR /workspace\n\n# Get Delve\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=\"$TARGETARCH\" go install github.com/go-delve/delve/cmd/dlv@latest\n\nCOPY go.mod go.mod\nCOPY go.sum go.sum\nRUN go mod download\n\n# Copy the go source\nCOPY ../main.go main.go\nCOPY cmd cmd/\nCOPY config config/\nCOPY persistence persistence/\nCOPY server server/\nCOPY handler handler/\nCOPY crypto crypto/\nCOPY dto dto/\nCOPY ee/saml ee/saml/\nCOPY session session/\nCOPY mail mail/\nCOPY audit_log audit_log/\nCOPY pagination pagination/\nCOPY rate_limiter rate_limiter/\nCOPY thirdparty thirdparty/\nCOPY build_info build_info/\nCOPY middleware middleware/\nCOPY flow_api flow_api/\nCOPY flowpilot flowpilot/\nCOPY template template/\nCOPY utils utils/\nCOPY mapper mapper/\nCOPY webhooks webhooks/\n\n# Build\nRUN go generate ./...\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=\"$TARGETARCH\" go build -gcflags=\"all=-N -l\" -a -o hanko main.go\n\n# Use distroless as minimal base image to package hanko binary\n# See https://github.com/GoogleContainerTools/distroless for details\nFROM gcr.io/distroless/static:nonroot\nWORKDIR /\nCOPY --from=builder /go/bin/dlv .\nCOPY --from=builder /workspace/hanko .\nUSER 65532:65532\n\nEXPOSE 8000 8001 40000\n\nENTRYPOINT [\"/dlv\", \"--listen=:40000\", \"--headless=true\", \"--api-version=2\", \"--accept-multiclient\", \"exec\", \"/hanko\", \"--\"]\n"
  },
  {
    "path": "backend/README.md",
    "content": "# Hanko backend\n\nHanko backend provides an HTTP API to build a modern login and registration experience for your users. Its core features\nare an API for passkeys (WebAuthn), passwords, and passcodes, as well as JWT management.\n\nHanko backend can be used on its own or in combination with [hanko-elements](../frontend/elements), a powerful frontend library\nthat contains polished and customizable UI flows for password-based and passwordless user authentication that can be\neasily integrated into any web app with as little as two lines of code.\n\n# Contents\n\n- [API features](#api-features)\n- [Running the backend](#running-the-backend)\n- [Running tests](#running-tests)\n- [Additional topics](#additional-topics)\n  - [Enabling password authentication](#enabling-password-authentication)\n  - [Cross-domain communication](#cross-domain-communication)\n  - [Audit logs](#audit-logs)\n  - [Rate Limiting](#rate-limiting)\n  - [Social connections](#social-connections)\n    - [Built-in providers](#built-in-providers)\n    - [Custom OAuth/OIDC providers](#custom-oauthoidc-providers)\n    - [Account linking](#account-linking)\n  - [User metadata](#user-metadata)\n  - [User import](#user-import)\n  - [Webhooks](#webhooks)\n  - [Session JWT templates](#session-jwt-templates)\n- [API specification](#api-specification)\n- [Configuration reference](#configuration-reference)\n- [License](#license)\n\n## API features\n\n- Passkeys (WebAuthn)\n- Passcodes\n- Passwords\n- Email verification\n- 2FA (TOTP, security keys)\n- JWT management\n- Sessions\n- User management\n- OAuth/OIDC SSO identity providers\n- SAML\n- Webhooks\n\n## Running the backend\n\n> **Note** If you just want to jump right into the experience of passkeys and passcodes, head over to the\n> [quickstart guide](../quickstart/README.md).\n\nTo get the Hanko backend up and running you need to:\n\n1. [Run a database](#run-a-database)\n2. [Configure database access](#configure-database-access)\n3. [Apply database migrations](#apply-database-migrations)\n4. [Run and configure an SMTP server](#run-and-configure-an-smtp-server)\n5. [Configure JSON Web Key Set generation](#configure-json-web-key-set-generation)\n6. [Configure WebAuthn](#configure-webauthn)\n7. [Configure CORS](#configure-cors)\n8. [Start the backend](#start-the-backend)\n\n### Run a database\n\nThe following databases are currently supported:\n\n- PostgreSQL\n- MySQL\n\n#### Postgres\n\nUse Docker to run a container based on the official [Postgres](https://hub.docker.com/_/postgres) image:\n\n```shell\ndocker run --name=postgres \\\n-e POSTGRES_USER=<DB_USER> \\\n-e POSTGRES_PASSWORD=<DB_PASSWORD> \\\n-e POSTGRES_DB=<DB_DATABASE> \\\n-p <DB_PORT>:5432 \\\n-d postgres\n```\n\nor use the [official binary packages](https://www.postgresql.org/download/) to install and run\na Postgres instance.\n\n#### MySQL\n\nUse Docker to run a container based on the official [MySQL](https://hub.docker.com/_/mysql) image:\n\n```shell\ndocker run --name=mysql \\\n-e MYSQL_USER=<DB_USER> \\\n-e MYSQL_PASSWORD=<DB_PASSWORD> \\\n-e MYSQL_DATABASE=<DB_DATABASE> \\\n-e MYSQL_RANDOM_ROOT_PASSWORD=true \\\n-p <DB_PORT>:3306 \\\n-d mysql:latest\n```\n\nor follow the official [installation instructions](https://dev.mysql.com/doc/mysql-getting-started/en/#mysql-getting-started-installing) to install and run\na MySQL instance.\n\n### Configure database access\n\nOpen the `config.yaml` file in the `backend/config` or create your own `*.yaml` file and add the following:\n\n```yaml\ndatabase:\n  user: <DB_USER>\n  password: <DB_PASSWORD>\n  host: localhost # change this if the DB is not running on localhost, esp. in a production setting\n  port: <DB_PORT>\n  database: <DB_DATABASE>\n  dialect: <DB_DIALECT> # depending on your choice of DB: postgres, mysql\n```\n\nReplace `<DB_USER>`, `<DB_PASSWORD>`, `<DB_PORT>`, `<DB_DATABASE>` with the values used in your running\nDB instance (cf. the Docker commands above used for running the DB containers) and replace `<DB_DIALECT>` with\nthe DB of your choice.\n\n### Apply Database migrations\n\nBefore you can start and use the service you need to run the database migrations:\n\n#### Docker\n\n```shell\ndocker run --mount type=bind,source=<PATH-TO-CONFIG-FILE>,target=/config/config.yaml -p 8000:8000 -it ghcr.io/teamhanko/hanko:latest migrate up\n```\n\n> **Note** The `<PATH-TO-CONFIG-FILE>` must be an absolute path to your config file created above.\n\n#### From source\n\nFirst build the Hanko backend. The only prerequisite is to have Go (v1.18+) [installed](https://go.dev/doc/install)\non your computer.\n\n```shell\ngo generate ./...\ngo build -a -o hanko main.go\n```\n\nThis command will create an executable with the name `hanko`, which then can be used to apply the database migrations\nand start the Hanko backend.\n\nTo apply the migrations, run:\n\n```shell\n./hanko migrate up --config <PATH-TO-CONFIG-FILE>\n```\n\n> **Note** The path to the config file can be relative or absolute.\n\n\n### Run and configure an SMTP server\n\nThe Hanko backend requires an SMTP server to send out mails containing\npasscodes (e.g. for the purpose of email verification, password recovery).\n\nFor local development purposes you can use, e.g., [Mailslurper](https://www.mailslurper.com/).\nFollow the official [installation](https://github.com/mailslurper/mailslurper/wiki/Getting-Started) instructions or\nuse an (inofficial) [Docker image](https://hub.docker.com/r/marcopas/docker-mailslurper) to get it up and running:\n\n```shell\ndocker run --name=mailslurper -it -p 2500:2500 -p 8080:8080 -p 8085:8085 @marcopas/docker-mailslurper\n```\n\nwhere in this case\n- `2500` is the SMTP port of the service\n- `8080` is the port for the GUI application for managing mails\n- `8085` is the port for the [API](https://github.com/mailslurper/mailslurper/wiki/API-Guide) service for managing mails\n\nWhen using the above Docker command to run a Mailslurper container, it does not configure\na user/password, so a minimal configuration in your configuration file (`backend/config/config.yaml` or\nyour own `*.yaml` file) could contain the following:\n\n```yaml\nemail_delivery:\n  enabled: true\n  email:\n    from_address: no-reply@example.com\n    from_name: Example Application\n  smtp:\n    host: localhost\n    port: 2500\n```\n\nTo ensure that passcode emails also contain a proper subject header, configure a service\nname:\n\n```yaml\nservice:\n  name: Example Authentication Service\n```\n\nIn a production setting you would rather use a self-hosted SMTP server or a managed service like AWS SES. In that case\nyou need to supply the `email_delivery.smtp.host`, `email_delivery.smtp.port` as well as the `email_delivery.smtp.user`,\n`email_delivery.smtp.password` settings according to your server/service settings.\n\n### Configure JSON Web Key Set generation\n\nThe API uses [JSON Web Tokens](https://www.rfc-editor.org/rfc/rfc7519.html) (JWTs) for\n[authentication](https://docs.hanko.io/api-reference/public/introduction).\nJWTs are verified using [JSON Web Keys](https://www.rfc-editor.org/rfc/rfc7517) (JWK).\nJWKs are created internally by setting `secrets.keys` options in the\nconfiguration file (`backend/config/config.yaml` or your own `*.yaml` file):\n\n```yaml\nsecrets:\n  keys:\n    - <CHANGE-ME>\n```\n\n> **Note**  at least one `secrets.keys` entry must be provided and each entry must be a random generated string at least 16 characters long.\n\nKeys secrets are used to en- and decrypt the JWKs which get used to sign the JWTs.\nFor every key a JWK is generated, encrypted with the key and persisted in the database.\n\nThe Hanko backend API publishes public cryptographic keys as a JWK set through the `.well-known/jwks.json`\n[endpoint](https://docs.hanko.io/api-reference/public/well-known/get-json-web-key-set) to enable clients to verify token\nsignatures.\n\n### Configure WebAuthn\n\nPasskeys are based on the [Web Authentication API](https://www.w3.org/TR/webauthn-2/#web-authentication-api).\nIn order to create and login with passkeys, the Hanko backend must be provided information about\nthe [WebAuthn Relying Party](https://www.w3.org/TR/webauthn-2/#webauthn-relying-party).\n\nFor most use cases, you just need the domain of your web application that uses the Hanko backend. Set\n`webauthn.relying_party.id` to the domain and set `webauthn.relying_party.origin` to the domain _including_ the\nprotocol.\n\n> **Important**: If you are hosting your web application on a non-standard HTTP port (i.e. `80`) you also have to\n> include this in the origin setting.\n\n#### Local development example\n\nWhen developing locally, the Hanko backend defaults to:\n\n```yaml\nwebauthn:\n  relying_party:\n    id: \"localhost\"\n    display_name: \"Hanko Authentication Service\"\n    origins:\n      - \"http://localhost\"\n```\n\nso no further configuration changes need to be made to your configuration file.\n\n#### Production Examples\n\nWhen you have a website hosted at `example.com` and you want to add a login to it that will be available\nat `https://example.com/login`, the WebAuthn config would look like this:\n\n```yaml\nwebauthn:\n  relying_party:\n    id: \"example.com\"\n    display_name: \"Example Project\"\n    origins:\n      - \"https://example.com\"\n```\n\nIf the login should be available at `https://login.example.com` instead, then the WebAuthn config would look like this:\n\n```yaml\nwebauthn:\n  relying_party:\n    id: \"login.example.com\"\n    display_name: \"Example Project\"\n    origins:\n      - \"https://login.example.com\"\n```\n\nGiven the above scenario, you still may want to bind your users WebAuthn credentials to `example.com` if you plan to\nadd other services on other subdomains later that should be able to use existing credentials. Another reason can be if\nyou want to have the option to move your login from `https://login.example.com` to `https://example.com/login` at some\npoint. Then the WebAuthn config would look like this:\n\n```yaml\nwebauthn:\n  relying_party:\n    id: \"example.com\"\n    display_name: \"Example Project\"\n    origins:\n      - \"https://login.example.com\"\n```\n\n### Configure CORS\n\nBecause the backend and your application(s) consuming backend API most likely have different origins, i.e.\nscheme (protocol), hostname (domain), and port part of the URL are different, you need to configure\nCross-Origin Resource Sharing (CORS) and specify your application(s) as allowed origins:\n\n```yaml\nserver:\n  public:\n    cors:\n      allow_origins:\n        - https://example.com\n```\n\nWhen you include a wildcard `*` origin you need to set `unsafe_wildcard_origin_allowed: true`:\n\n```yaml\nserver:\n  public:\n    cors:\n      allow_origins:\n        - \"*\"\n      unsafe_wildcard_origin_allowed: true\n```\n\nWildcard `*` origins can lead to cross-site attacks and when you include a `*` wildcard origin,\nwe want to make sure, that you understand what you are doing, hence this flag.\n\n> **Note** In most cases, the `allow_origins` list here should contain the same entries as the `webauthn.relying_party.origins` list. Only when you have an Android app you will have an extra entry (`android:apk-key-hash:...`) in the `webauthn.relying_party.origins` list.\n\n### Start the backend\n\nThe Hanko backend consists of a public and an administrative API (currently providing user management\nendpoints). These can be started separately or in a single command.\n\n#### Start the public API\n\n##### Docker\n\n```shell\ndocker run --mount type=bind,source=<PATH-TO-CONFIG-FILE>,target=/config/config.yaml -p 8000:8000 -it ghcr.io/teamhanko/hanko:latest serve public\n```\n\n##### Using pre-built binaries\n\nEach [GitHub release](https://github.com/teamhanko/hanko/releases) (> 0.9.0) has `hanko`'s binary assets uploaded to it. Alternatively you can use\na tool like [eget](https://github.com/zyedidia/eget) to install binaries from releases on GitHub:\n\n```bash\neget teamhanko/hanko\n```\n\n##### From source\n\n```shell\ngo generate ./...\ngo build -a -o hanko main.go\n```\n\nThen run:\n```shell\n./hanko serve public --config <PATH-TO-CONFIG-FILE>\n```\n\n> **Note** The `<PATH-TO-CONFIG-FILE>` must be an absolute path to your config file created above.\n\n`8000` is the default port for the public API. It can\nbe [customized](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-public#address) in the\nconfiguration through the `server.public.address` option.\n\nThe service is now available at `localhost:8000`.\n\n#### Start the admin API\n\nIn the usage section above we only started the public API. Use the command below to start the admin API. The default\nport is `8001`, but can be\n[customized](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-admin) in the configuration\nthrough the `server.admin.address` option.\n\n```shell\nserve admin\n```\n\n> **Warning** The admin API must be protected by an access management system.\n\n##### Start both public and admin API\n\nUse this command to start the public and admin API together:\n\n```shell\nserve all\n```\n\n## Running tests\n\nYou can run the unit tests by running the following command within the `backend` directory:\n\n```bash\ngo test -v ./...\n```\n\n## Additional topics\n\n### Enabling password authentication\n\nPassword-based authentication is disabled per default. You can activate it and set the minimum password\nlength in your configuration file:\n\n```yaml\npassword:\n  enabled: true\n  min_password_length: 8\n```\n\n### Cross-domain communication\n\nJWTs used for authentication are propagated via cookie. If your application and the Hanko backend run on different\ndomains, cookies cannot be set by the Hanko backend. In that case the backend must be configured to transmit the JWT via\nHeader (`X-Auth-Token`). To do so, enable propagation of the `X-Auth-Token` header:\n\n```yaml\nsession:\n  enable_auth_token_header: true\n```\n\n### Audit logs\n\nAPI operations are recorded in an audit log. By default, the audit log is enabled\nand logs to STDOUT:\n\n```yaml\naudit_log:\n  console_output:\n    enabled: true\n    output: \"stdout\"\n  storage:\n    enabled: false\n```\n\nTo persist audit logs in the database, set `audit_log.storage.enabled` to `true`.\n\n### Rate Limiting\n\nHanko implements basic fixed-window rate limiting for the passcode/init and password/login endpoints to mitigate brute-force attacks.\nIt uses a combination of user-id/IP to mitigate DoS attacks on user accounts. You can choose between an in-memory and a redis store.\n\nIn production systems, you may want to hide the\nHanko service behind a proxy or gateway (e.g. Kong, Traefik) to provide additional network-based rate limiting.\n\n### Social connections\n\nHanko supports OAuth-based ([authorization code flow](https://www.rfc-editor.org/rfc/rfc6749#section-1.3.1)) third\nparty provider logins. The `third_party` configuration\n[option](https://github.com/teamhanko/hanko/wiki/config-properties-third_party) contains all relevant configuration.\nThis includes options for setting up redirect URLs (in case of success or error on authentication with a provider) that\napply to both [built-in](#built-in-providers) and\n[custom](#custom-oauthoidc-providers) providers.\n\n\n#### Built-in providers\n\nBuilt-in providers can be configured through the `third_party.providers` configuration [option](https://github.com/teamhanko/hanko/wiki/config-properties-third_party).\nThey must be explicitly `enabled` (i.e. providers are disabled default).\nAll provider configurations require provider credentials in the form of a client ID (`client_id`)\nand a client secret (`secret`). See the guides in the official documentation for instructions on how to obtain these:\n\n- [Apple](https://docs.hanko.io/guides/authentication-methods/oauth/apple)\n- [Discord](https://docs.hanko.io/guides/authentication-methods/oauth/discord)\n- [GitHub](https://docs.hanko.io/guides/authentication-methods/oauth/github)\n- [Google](https://docs.hanko.io/guides/authentication-methods/oauth/google)\n- [LinkedIn](https://docs.hanko.io/guides/authentication-methods/oauth/linkedin)\n- [Microsoft](https://docs.hanko.io/guides/authentication-methods/oauth/microsoft)\n\n#### Custom OAuth/OIDC providers\n\nCustom providers can be configured through the `third_party.custom_providers` configuration\n[option](https://github.com/teamhanko/hanko/wiki/config-properties-third_party-properties-custom_providers).\nLike built-in providers they must be explicitly `enabled` and require a `client_id` and `secret`, which must\nbe obtained from the respective provider.\nCustom providers can use either OAuth or OIDC. OIDC providers can be configured to use\n[OIDC Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) by setting the `use_discovery`\noption to `true`. An `issuer` must be configured too in that case. Otherwise both OAuth and OIDC providers\ncan manually define required endpoints (`authorization_endpoint`, `token_endpoint`, `userinfo_endpoint`).\n`scopes` must be explicitly defined (with `openid` being the minimum requirement in case of OIDC providers).\n\n#### Account linking\n\nThe `allow_linking` configuration option for built-in and custom providers determines whether automatic account linking for this provider\nis activated. Note that account linking is based on e-mail addresses and OAuth providers may allow account holders to\nuse unverified e-mail addresses or may not provide any information at all about the verification status of e-mail\naddresses. This poses a security risk and potentially allows bad actors to hijack existing Hanko\naccounts associated with the same address. It is therefore recommended to make sure you trust the provider and to\nalso enable `emails.require_verification` in your configuration to ensure that only verified third party provider\naddresses may be used.\n\n### User metadata\n\nHanko allows for defining arbitrary user metadata. Metadata can be categorized into\nthree types that differ as to how they can be accessed and modified:\n\n| Metadata type | Public API                   | Admin API             |\n|---------------|------------------------------|-----------------------|\n| Private       | No read or write access      | Read and write access |\n| Public        | Read access                  | Read and write access |\n| Unsafe        | Read access and write access | Read and write access |\n\nEach metadata type supports a maximum of 3,000 characters. Metadata is stored as compact JSON (whitespace is ignored).\nJSON syntax characters (`{`, `:`, `\"`, `}`) count toward the character limit.\nMultibyte UTF-8 characters (like emojis or non-Latin characters) count as 1 character each.\n\n#### Private metadata\n\nPrivate metadata should be used for sensitive data that should not be exposed to the client (e.g., internal flags/ids,\nconfiguration, or access control details).\n\nPrivate metadata can be read through the Admin API only using the\n[Get metadata of a user](/api-reference/admin/user-management/get-metadata-of-a-user)\nendpoint.\n\nPrivate metadata can be set and modified through the Admin API only by using the\n[Patch metadata of a user](https://docs.hanko.io/api-reference/admin/user-management/patch-metadata-of-a-user) endpoint.\n\n#### Public metadata\n\nPublic metadata should be used for non-sensitive information that you want accessible but not modifiable by the client\n(e.g., certain user roles, UI preferences, display options).\n\nPublic metadata can be read through the Public API, the Admin API and in JWT templates for customizing\nthe session JWT:\n\n- `Public API`:\n  - Public metadata is returned in the `user` object in the payload on the `success` state in a\n    [Login](https://docs.hanko.io/api-reference/flow/login) and\n    [Registration](https://docs.hanko.io/api-reference/flow/registration) flow as well\n    as in the payload on the `profile_init` state in a [Profile](https://docs.hanko.io/api-reference/flow/profile) flow.\n  - Public metadata is returned as part of the response of the\n    [Get a user by ID](https://docs.hanko.io/api-reference/public/user-management/get-a-user-by-id) endpoint.\n- `Admin API`:\n  - Public metadata is returned as part of the response of the\n    [Get metadata of a user](https://docs.hanko.io/api-reference/admin/user-metadata-management/get-metadata-of-a-user)\n    endpoint.\n  - Public metadata is returned as part of the response of the\n    [Get a user by ID](https://docs.hanko.io/api-reference/admin/user-management/get-a-user-by-id) endpoint.\n- `JWT Templates`:\n  - Public metadata can be accessed through the `User` context object available on session JWT customization.\n    See [Session JWT templates](#session-jwt-templates) for more details.\n\nPublic metadata can be set and modified through the Admin API only by using the\n[Patch metadata of a user](https://docs.hanko.io/api-reference/admin/user-management/patch-metadata-of-a-user) endpoint.\n\n#### Unsafe metadata\n\nUnsafe metadata should be used for non-sensitive, temporary or experimental data that doesn't need strong safety\nguarantees.\n\nUnsafe metadata can be read through the Public API, the Admin API and in JWT templates for customizing\nthe session JWT:\n\n- `Public API`:\n    - Unsafe metadata is returned in the `user` object in the payload on the `success` state in a\n      [Login](https://docs.hanko.io/api-reference/flow/login) and\n      [Registration](https://docs.hanko.io/api-reference/flow/registration) flow as well\n      as in the payload on the `profile_init` state in a [Profile](https://docs.hanko.io/api-reference/flow/profile) flow.\n    - Unsafe metadata is returned as part of the response of the\n      [Get a user by ID](https://docs.hanko.io/api-reference/public/user-management/get-a-user-by-id) endpoint.\n- `Admin API`:\n    - Unsafe metadata is returned as part of the response of the\n      [Get metadata of a user](https://docs.hanko.io/api-reference/admin/user-metadata-management/get-metadata-of-a-user)\n      endpoint.\n    - Unsafe metadata is returned as part of the response of the\n      [Get a user by ID](https://docs.hanko.io/api-reference/admin/user-management/get-a-user-by-id) endpoint.\n- `JWT Templates`:\n    - Unsafe metadata can be accessed through the `User` context object available on session JWT customization.\n      See [Session JWT templates](#session-jwt-templates) for more details.\n\nUnsafe metadata can be set and modified through the Public API and the Admin API:\n\n- `Public API`:\n  - Unsafe metadata can be set using the `patch_metadata` action in the\n    [Profile](https://docs.hanko.io/api-reference/flow/profile) flow.\n\n- `Admin API`:\n  - Unsafe metadata can be set using the\n    [Patch metadata of a user](https://docs.hanko.io/api-reference/admin/user-management/patch-metadata-of-a-user)\n    endpoint.\n\n\n\n### User import\nYou can import an existing user pool into Hanko using json in the following format:\n```json\n[\n  {\n    \"user_id\": \"799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3\",\n    \"emails\": [\n      {\n        \"address\": \"koreyrath@wolff.name\",\n        \"is_primary\": true,\n        \"is_verified\": true\n      }\n    ],\n    \"created_at\": \"2023-06-07T13:42:49.369489Z\",\n    \"updated_at\": \"2023-06-07T13:42:49.369489Z\"\n  },\n  {\n    \"user_id\": \"\",\n    \"emails\": [\n      {\n        \"address\": \"joshuagrimes@langworth.name\",\n        \"is_primary\": true,\n        \"is_verified\": true\n      }\n    ],\n    \"created_at\": \"2023-06-07T13:42:49.369494Z\",\n    \"updated_at\": \"2023-06-07T13:42:49.369494Z\"\n  }\n]\n```\nThere is a json schema file located [here](json_schema/hanko.user_import.json) that you can use for validation and input suggestions.\nTo import users run:\n\n> hanko user import -i ./path/to/import_file.json\n\n\n### Webhooks\n\nWebhooks are an easy way to get informed about changes in your Hanko instance (e.g. user or email updates).\nTo use webhooks you have to provide an endpoint on your application which can process the events. Please be aware that your\nendpoint need to respond with an HTTP status code 200. Else-wise the delivery of the event will not be counted as successful.\n\n#### Events\nWhen a webhook is triggered it will send you a **JSON** body which contains the event and a jwt.\nThe JWT contains 2 custom claims:\n\n* **data**: contains the whole object for which the change was made. (e.g.: the whole user object when an email or user is changed/created/deleted)\n* **evt**: the event for which the webhook was triggered\n\nA typical webhook event looks like:\n\n```json\n{\n  \"token\": \"the-jwt-token-which-contains-the-data\",\n  \"event\": \"name of the event\"\n}\n```\n\nTo decode the webhook you can use the JWKs created in [Configure JSON Web Key Set generation](#configure-json-web-key-set-generation)\n\n#### Event Types\n\nHanko sends webhooks for the following event types:\n\n| Event                       | Triggers on                                                                                        |\n|-----------------------------|----------------------------------------------------------------------------------------------------|\n| user                        | user creation, user deletion, user update, email creation, email deletion, change of primary email |\n| user.create                 | user creation                                                                                      |\n| user.delete                 | user deletion                                                                                      |\n| user.login                  | user login                                                                                         |\n| user.update                 | user update, email creation, email deletion, change of primary email                               |\n| user.update.email           | email creation, email deletion, change of primary email                                            |\n| user.update.email.create    | email creation                                                                                     |\n| user.update.email.delete    | email deletion                                                                                     |\n| user.update.email.primary   | change of primary email                                                                            |\n| user.update.username.create | username creation                                                                                  |\n| user.update.username.delete | username deletion                                                                                  |\n| user.update.username.update | change of username                                                                                 |\n| email.send                  | an email was sent or should be sent                                                                |\n\nAs you can see, events can have subevents. You are able to filter which events you want to receive by either selecting\na parent event when you want to receive all subevents or selecting specific subevents.\n\n#### Enabling Webhooks\n\nYou can activate webhooks by adding the following snippet to your configuration file:\n\n```yaml\nwebhooks:\n  enabled: true\n  hooks:\n    - callback: <YOUR WEBHOOK ENDPOINT>\n      events:\n        - user\n```\n\n### Session JWT templates\n\nYou can define custom claims that will be added to session JWTs through the `session.jwt_template.claims`\nconfiguration option.\n\nThese claims are processed at JWT generation time and can include static values,\ntemplated strings using Go's text/template syntax, or nested structures (maps and slices).\n\nThe template has access to user data via the `.User` field, which includes:\n- `.User.UserID`: The user's unique ID (string)\n- `.User.Email`: Email details (optional)\n  - `User.Email.Address`: The actual email address\n  - `User.Email.IsPrimary`: Whether this email address is the primary email address of this user\n  - `User.Email.IsVerified`: Whether this email address has been verified by the user\n- `.User.FamilyName`: The user's family name (string, optional)\n- `.User.GivenName`: The user's given name (string, optional)\n- `.User.Name`: The user's full name (string, optional)\n- `.User.Picture`: The user's profile picture URL (string, optional)\n- `.User.Username`: The user's username (string, optional)\n- `.User.Metadata`: The user's public and unsafe metadata (optional)\n    - `.User.Metadata.Public`: The user's public metadata (object)\n    - `.User.Metadata.Unsafe`: The user's unsafe metadata (object)\n\n#### Accessing user metadata\n\n`.User.Metadata.Public` and `.User.Metadata.Unsafe`  can be accessed and queried using\n[GJSON Path Syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) (try it out in the\n[playground](https://gjson.dev/)).\n\nAssume that a user's public metadata consisted of the following data:\n\n```json\n{\n    \"display_name\": \"GamerDude\",\n    \"favorite_games\": [\n        {\n            \"name\": \"Legends of Valor\",\n            \"genre\": \"RPG\",\n            \"playtime_hours\": 142.3\n        },\n        {\n            \"name\": \"Space Raiders\",\n            \"genre\": \"Sci-Fi Shooter\",\n            \"playtime_hours\": 87.6\n        }\n    ]\n}\n```\n\nThen you could, for example, access this data in the following ways in your templates:\n\n```yaml\ndisplay_name: '{{ .User.Metadata.Public \"display_name\" }}'\nfavorite_games: '{{ .User.Metadata.Public \"favorite_games\" }}'\nfavorite_games_with_playtime_over_100: '{{ .User.Metadata.Public \"favorite_games.#(playtime_hours>100)\" }}'\nfavorite_genres: '{{ .User.Metadata.Public \"favorite_games.#.genre\" }}'\n```\n\n> **Note**\n>\n> Ensure you use proper quoting when accessing metadata. `.User.Metadata.Public` and `.User.Metadata.Unsafe`\nare function calls internally and the given path argument must be a string, so it must be double quoted.\nIf you use use double quotes for your entire claim template then the path argument must be escaped, i.e.:\n`\"{{ .User.Metadata.Public \\\"display_name\\\" }}\"`\n\n\nExample usage in YAML configuration:\n```yaml\nrole: \"user\"                                           # Static value\nuser_email: \"{{.User.Email.Address}}\"                  # Templated string\nis_verified: \"{{.User.Email.IsVerified}}\"              # Boolean from user data\nmetadata:                                              # Nested map\n  greeting: \"Hello {{.User.Username}}\"\n  source: '{{ .User.Metadata.Public \"display_name\" }}' # Data read from public metadata\n  ui_theme: '{{ .User.Metadata.Unsafe \"ui_theme\" }}'   # Data read from unsafe metadata\nscopes:                                                # Slice with templated value\n    - \"read\"\n    - \"write\"\n    - \"{{if .User.Email.IsVerified}}admin{{else}}basic{{end}}\"\n```\n\nIn this example:\n- `role` is a static string (\"user\").\n- `user_email` dynamically inserts the user's email address.\n- `is_verified` inserts a boolean indicating email verification status.\n- `metadata` is a nested map with a static `source` and a templated `greeting`.\n- `scopes` is a slice combining static values and a conditional template.\n\nNotes:\n- Custom claims are added at the top level of the session token [payload](#jwt-payload).\n- Claims with the following keys will be ignored because they are currently added to the JWT by default:\n    - `sub`\n    - `iat`\n    - `exp`\n    - `aud`\n    - `iss`\n    - `email`\n    - `username`\n    - `session_id`\n- Templates must conform to valid [Go text/template syntax](https://pkg.go.dev/text/template). Invalid templates are\n  logged and excluded from the generated token.\n- Boolean strings (\"true\" or \"false\") from templates are automatically converted to actual booleans.\n\nFor more details on template syntax, see: https://pkg.go.dev/text/template\n\n## API specification\n\n- [Hanko Public API](https://docs.hanko.io/api-reference/public/introduction)\n- [Hanko Admin API](https://docs.hanko.io/api-reference/admin/introduction)\n\n## Configuration reference\n\n- [Using configuration file](https://github.com/teamhanko/hanko/wiki/Using-configuration-file)\n- [Using environment variables](https://github.com/teamhanko/hanko/wiki/Using-environment-variables)\n- [Configuration reference](https://github.com/teamhanko/hanko/wiki/config)\n\n\n## License\n\nThe Hanko backend ist licensed under the [AGPL-3.0](../LICENSE).\n"
  },
  {
    "path": "backend/audit_log/logger.go",
    "content": "package auditlog\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\tzeroLog \"github.com/rs/zerolog\"\n\tzeroLogger \"github.com/rs/zerolog/log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n)\n\ntype Logger interface {\n\tCreate(echo.Context, models.AuditLogType, *models.User, error, ...DetailOption) error\n\tCreateWithConnection(*pop.Connection, echo.Context, models.AuditLogType, *models.User, error, ...DetailOption) error\n}\n\ntype logger struct {\n\tpersister             persistence.Persister\n\tstorageEnabled        bool\n\tlogger                zeroLog.Logger\n\tconsoleLoggingEnabled bool\n\tmustMask              bool\n}\n\nfunc NewLogger(persister persistence.Persister, cfg config.AuditLog) Logger {\n\tvar loggerOutput *os.File = nil\n\tswitch cfg.ConsoleOutput.OutputStream {\n\tcase config.OutputStreamStdOut:\n\t\tloggerOutput = os.Stdout\n\tcase config.OutputStreamStdErr:\n\t\tloggerOutput = os.Stderr\n\tdefault:\n\t\tloggerOutput = os.Stdout\n\t}\n\n\treturn &logger{\n\t\tpersister:             persister,\n\t\tstorageEnabled:        cfg.Storage.Enabled,\n\t\tlogger:                zeroLog.New(loggerOutput),\n\t\tconsoleLoggingEnabled: cfg.ConsoleOutput.Enabled,\n\t\tmustMask:              cfg.Mask,\n\t}\n}\n\ntype DetailOption func(map[string]interface{})\n\nfunc Detail(key string, value interface{}) DetailOption {\n\treturn func(d map[string]interface{}) {\n\t\tif value != \"\" || value != nil {\n\t\t\td[key] = value\n\t\t}\n\t}\n}\n\nfunc (l *logger) Create(context echo.Context, auditLogType models.AuditLogType, user *models.User, logError error, detailOpts ...DetailOption) error {\n\treturn l.CreateWithConnection(l.persister.GetConnection(), context, auditLogType, user, logError, detailOpts...)\n}\n\nfunc (l *logger) CreateWithConnection(tx *pop.Connection, context echo.Context, auditLogType models.AuditLogType, user *models.User, logError error, detailOpts ...DetailOption) error {\n\tdetails := make(map[string]interface{})\n\tfor _, detailOpt := range detailOpts {\n\t\tdetailOpt(details)\n\t}\n\n\tauditLog, err := models.NewAuditLog(auditLogType, l.getRequestMeta(context), details, user, logError)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif l.mustMask {\n\t\tauditLog = l.mask(auditLog)\n\t}\n\n\tif l.storageEnabled {\n\t\terr = l.store(tx, auditLog)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif l.consoleLoggingEnabled {\n\t\tl.logToConsole(auditLog)\n\t}\n\n\treturn nil\n}\n\nfunc (l *logger) store(tx *pop.Connection, auditLog models.AuditLog) error {\n\treturn l.persister.GetAuditLogPersisterWithConnection(tx).Create(auditLog)\n}\n\nfunc (l *logger) logToConsole(auditLog models.AuditLog) {\n\tvar err string\n\tif auditLog.Error != nil {\n\t\terr = *auditLog.Error\n\t}\n\n\tnow := time.Now()\n\tloggerEvent := zeroLogger.Log().\n\t\tStr(\"audience\", \"audit\").\n\t\tStr(\"type\", string(auditLog.Type)).\n\t\tStr(\"error\", err).\n\t\tStr(\"http_request_id\", auditLog.MetaHttpRequestId).\n\t\tStr(\"source_ip\", auditLog.MetaSourceIp).\n\t\tStr(\"user_agent\", auditLog.MetaUserAgent).\n\t\tAny(\"details\", auditLog.Details).\n\t\tStr(\"time\", now.Format(time.RFC3339Nano)).\n\t\tStr(\"time_unix\", strconv.FormatInt(now.Unix(), 10))\n\n\tif auditLog.ActorUserId != nil {\n\t\tloggerEvent.Str(\"user_id\", auditLog.ActorUserId.String())\n\t\tif auditLog.ActorEmail != nil {\n\t\t\tloggerEvent.Str(\"user_email\", *auditLog.ActorEmail)\n\t\t}\n\t}\n\n\tloggerEvent.Send()\n}\n\nfunc (l *logger) getRequestMeta(c echo.Context) models.RequestMeta {\n\treturn models.RequestMeta{\n\t\tHttpRequestId: c.Response().Header().Get(echo.HeaderXRequestID),\n\t\tUserAgent:     c.Request().UserAgent(),\n\t\tSourceIp:      c.RealIP(),\n\t}\n}\n\nfunc (l *logger) mask(auditLog models.AuditLog) models.AuditLog {\n\tif auditLog.ActorEmail != nil && *auditLog.ActorEmail != \"\" {\n\t\temail := utils.MaskEmail(*auditLog.ActorEmail)\n\t\tauditLog.ActorEmail = &email\n\t}\n\n\tfor key, value := range auditLog.Details {\n\t\tif key == \"username\" {\n\t\t\tauditLog.Details[key] = utils.MaskUsername(value.(string))\n\t\t}\n\n\t\tif key == \"email\" {\n\t\t\tauditLog.Details[key] = utils.MaskEmail(value.(string))\n\t\t}\n\t}\n\n\treturn auditLog\n}\n"
  },
  {
    "path": "backend/build_info/build_info.go",
    "content": "package build_info\n\nimport (\n\t_ \"embed\"\n\t\"runtime/debug\"\n\t\"strings\"\n)\n\n//go:generate sh -c \"git describe --tags --always --match backend/* | sed -e s#^backend/## > version.txt\"\n//go:embed version.txt\nvar version string\nvar realVersion *string\nvar isDirty *bool\n\nfunc GetVersion() string {\n\tif realVersion == nil {\n\t\ttempVersion := strings.TrimSpace(version)\n\t\tif getIsDirty() {\n\t\t\ttempVersion += \"-dirty\"\n\t\t}\n\t\trealVersion = &tempVersion\n\t}\n\treturn *realVersion\n}\n\nfunc getIsDirty() bool {\n\tif isDirty == nil {\n\t\tbi, ok := debug.ReadBuildInfo()\n\t\tif ok {\n\t\t\tmodified := false\n\t\t\tfor _, v := range bi.Settings {\n\t\t\t\tif v.Key == \"vcs.modified\" {\n\t\t\t\t\tif v.Value == \"true\" {\n\t\t\t\t\t\tmodified = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tisDirty = &modified\n\t\t}\n\t}\n\treturn *isDirty\n}\n"
  },
  {
    "path": "backend/cmd/cleanup/cleanup.go",
    "content": "package cleanup\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"log\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n)\n\n// options holds user-provided CLI options\ntype options struct {\n\ttables     []string // List of tables to clean up\n\tconfigFile string   // Path to configuration file\n\tpageSize   int      // The number of entities to query at once\n\trun        bool     // Whether to execute cleanup or simulate\n}\n\n// handlerParam holds the necessary parameters for cleanup operations\ntype handlerParam struct {\n\ttable   string\n\tconfig  *config.Config\n\tstorage persistence.Storage\n\toptions *options\n}\n\n// handlerFunc defines the function signature for cleanup handlers\ntype handlerFunc func(handlerParam) error\n\n// Table names used for cleanup operations\nconst (\n\ttableAuditLogs           = \"audit_logs\"\n\ttableFlows               = \"flows\"\n\ttableWebauthnSessionData = \"webauthn_session_data\"\n)\n\n// Map of table names to their respective cleanup handlers\nvar handler = map[string]handlerFunc{\n\ttableFlows: func(param handlerParam) error {\n\t\treturn cleanup[models.Flow](param, param.storage.GetFlowPersister(), time.Now().UTC())\n\t},\n\ttableAuditLogs: func(param handlerParam) error {\n\t\tduration, err := time.ParseDuration(param.config.AuditLog.Retention)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to parse the retention duration: %w\", err)\n\t\t}\n\n\t\treturn cleanup[models.AuditLog](param, param.storage.GetAuditLogPersister(), time.Now().Add(-duration).UTC())\n\t},\n\ttableWebauthnSessionData: func(param handlerParam) error {\n\t\treturn cleanup[models.WebauthnSessionData](param, param.storage.GetWebauthnSessionDataPersister(), time.Now().UTC())\n\t},\n}\n\n// allowedTables is a list of table names that can be cleaned up\nvar allowedTables = func() []string {\n\tkeys := make([]string, 0, len(handler))\n\tfor key := range handler {\n\t\tkeys = append(keys, key)\n\t}\n\n\tsort.Strings(keys)\n\n\treturn keys\n}()\n\n// isTableAllowed checks if a given table name exists in the allowed list\nfunc isTableAllowed(table string) bool {\n\tfor _, allowed := range allowedTables {\n\t\tif table == allowed {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// validateTables checks if the specified table names exist in the allowed list\nfunc validateTables(tables []string) error {\n\tvar invalidTables []string\n\n\tfor _, table := range tables {\n\t\tif !isTableAllowed(table) {\n\t\t\tinvalidTables = append(invalidTables, table)\n\t\t}\n\t}\n\n\tif len(invalidTables) > 0 {\n\t\treturn fmt.Errorf(\"invalid table name(s): %s - allowed values: %s\",\n\t\t\tstrings.Join(invalidTables, \", \"), strings.Join(allowedTables, \", \"))\n\t}\n\n\treturn nil\n}\n\n// newCleanupCommand creates the Cobra command for database cleanup\nfunc newCleanupCommand() *cobra.Command {\n\topts := &options{}\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"cleanup\",\n\t\tShort: \"Cleanup the database.\",\n\t\tLong:  `Cleans up the database by deleting expired entities.`,\n\t\tPreRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif len(opts.tables) == 0 {\n\t\t\t\topts.tables = allowedTables\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\treturn validateTables(opts.tables)\n\t\t},\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tcfg, err := config.Load(&opts.configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tstorage, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tlog.Printf(\"Cleaning up table(s): %s...\\n\", strings.Join(opts.tables, \", \"))\n\n\t\t\tfor _, table := range opts.tables {\n\t\t\t\tparam := handlerParam{\n\t\t\t\t\ttable:   table,\n\t\t\t\t\tconfig:  cfg,\n\t\t\t\t\tstorage: storage,\n\t\t\t\t\toptions: opts,\n\t\t\t\t}\n\t\t\t\terr = handler[table](param)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlog.Println(\"Cleanup completed.\")\n\n\t\t\tif !opts.run {\n\t\t\t\tlog.Println(\"This was a dry-run; add --run to the command to really delete the data.\")\n\t\t\t}\n\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(&opts.configFile, \"config\", \"c\", config.DefaultConfigFilePath, \"path to config file\")\n\tcmd.Flags().StringSliceVarP(&opts.tables, \"tables\", \"t\", []string{}, fmt.Sprintf(\"specify individual tables to clean up (comma-separated) - allowed values: %s\", strings.Join(allowedTables, \", \")))\n\tcmd.Flags().IntVarP(&opts.pageSize, \"page-size\", \"s\", 512, \"the number of entities to query at once\")\n\tcmd.Flags().BoolVar(&opts.run, \"run\", false, \"execute the cleanup process instead of simulating\")\n\n\treturn cmd\n}\n\n// cleanup performs the cleanup operation for a given table and persister\nfunc cleanup[T any](param handlerParam, persister persistence.Cleanup[T], cutoffTime time.Time) error {\n\tvar (\n\t\tpage    = 1\n\t\tdeleted = 0\n\t)\n\n\tfor {\n\t\titems, err := persister.FindExpired(cutoffTime, page, param.options.pageSize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(items) > 0 {\n\t\t\tfor _, item := range items {\n\t\t\t\tif param.options.run {\n\t\t\t\t\terr = persister.Delete(item)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tdeleted++\n\t\t\t}\n\n\t\t\tlog.Printf(\"Deleted %d %s in total.\", deleted, param.table)\n\n\t\t\tif !param.options.run {\n\t\t\t\tpage++\n\t\t\t}\n\t\t}\n\n\t\tif len(items) < param.options.pageSize {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// RegisterCommands registers the cleanup command with the parent command\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := newCleanupCommand()\n\tparent.AddCommand(cmd)\n}\n"
  },
  {
    "path": "backend/cmd/isready/isready.go",
    "content": "package isready\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n)\n\nfunc NewIsReadyCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:       \"isready\",\n\t\tArgs:      cobra.OnlyValidArgs,\n\t\tValidArgs: []string{\"admin\", \"public\"},\n\t\tShort:     \"Health check a service\",\n\t\tLong: `Checks if the specified service is healthy. Possible values are \"admin\" and \"public\".\nUses the \"/health/ready\" endpoint to check if the service is ready to serve requests.\n\t\t`,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tif len(args) != 1 {\n\t\t\t\tlog.Fatalf(\"Please specify a service to check\")\n\t\t\t}\n\t\t\tservice := args[0]\n\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\taddress := \"\"\n\t\t\tif service == \"admin\" {\n\t\t\t\taddress = cfg.Server.Admin.Address\n\t\t\t}\n\t\t\tif service == \"public\" {\n\t\t\t\taddress = cfg.Server.Public.Address\n\t\t\t}\n\t\t\thost, port, err := net.SplitHostPort(address)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"Could not parse address %s\", address)\n\t\t\t}\n\t\t\tif host == \"\" {\n\t\t\t\thost = \"localhost\"\n\t\t\t}\n\t\t\trequestUrl := fmt.Sprintf(\"http://%s:%s/health/ready\", host, port)\n\t\t\tres, err := http.Get(requestUrl)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"Service %s is not ready\", service)\n\t\t\t} else {\n\t\t\t\tif res.StatusCode != 200 {\n\t\t\t\t\tlog.Fatalf(\"Service %s is not ready\", service)\n\t\t\t\t} else {\n\t\t\t\t\tlog.Println(fmt.Sprintf(\"Service %s is ready\", service))\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\n\treturn cmd\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := NewIsReadyCommand()\n\tparent.AddCommand(cmd)\n}\n"
  },
  {
    "path": "backend/cmd/jwk/create.go",
    "content": "package jwk\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk/local_db\"\n\n\t\"log\"\n)\n\nfunc NewCreateCommand() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"create\",\n\t\tShort: \"create JSON Web Key and print them in the console\",\n\t\tLong:  ``,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tfmt.Println(\"create called\")\n\t\t\tgenerator := local_db.RSAKeyGenerator{}\n\t\t\tkey, err := generator.Generate(\"key1\")\n\t\t\tif err != nil {\n\t\t\t\tlog.Panicln(err)\n\t\t\t}\n\t\t\tj, err := json.Marshal(key)\n\t\t\tif err != nil {\n\t\t\t\tlog.Panicln(err)\n\t\t\t}\n\t\t\tfmt.Println(string(j))\n\t\t},\n\t}\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/jwk/root.go",
    "content": "package jwk\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\n\nfunc NewMigrateCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"jwk\",\n\t\tShort: \"Tools for handling JSON Web Keys\",\n\t\tLong:  ``,\n\t}\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := NewMigrateCmd()\n\tparent.AddCommand(cmd)\n\tcmd.AddCommand(NewCreateCommand())\n}\n"
  },
  {
    "path": "backend/cmd/jwt/create.go",
    "content": "package jwt\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\nfunc NewCreateCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile string\n\t\tpretty     bool\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"create [user_id]\",\n\t\tShort: \"generate a JSON Web Token for a given user_id\",\n\t\tLong:  ``,\n\t\tArgs: func(cmd *cobra.Command, args []string) error {\n\t\t\tif len(args) < 1 {\n\t\t\t\treturn errors.New(\"user_id required\")\n\t\t\t}\n\t\t\tif _, err := uuid.FromString(args[0]); err != nil {\n\t\t\t\treturn errors.New(\"user_id is not a uuid\")\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tjwkManager, err := jwk.NewManager(cfg.Secrets, persister)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"failed to create jwk persister: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tsessionManager, err := session.NewManager(jwkManager, *cfg)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"failed to create session generator: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tuserId := uuid.FromStringOrNil(args[0])\n\n\t\t\tuserModel, err := persister.GetUserPersister().Get(userId)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"failed to get user from db: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttoken, rawToken, err := sessionManager.GenerateJWT(dto.UserJWTFromUserModel(userModel))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"failed to generate token: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tsessionID, _ := rawToken.Get(\"session_id\")\n\n\t\t\texpirationTime := rawToken.Expiration()\n\t\t\tsessionModel := models.Session{\n\t\t\t\tID:        uuid.FromStringOrNil(sessionID.(string)),\n\t\t\t\tUserID:    userId,\n\t\t\t\tCreatedAt: rawToken.IssuedAt(),\n\t\t\t\tUpdatedAt: rawToken.IssuedAt(),\n\t\t\t\tExpiresAt: &expirationTime,\n\t\t\t\tLastUsed:  rawToken.IssuedAt(),\n\t\t\t}\n\n\t\t\terr = persister.GetSessionPersister().Create(sessionModel)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"failed to store session: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfmt.Printf(\"Token: %s\\n\", token)\n\n\t\t\tif pretty {\n\t\t\t\trawTokenMap, err := rawToken.AsMap(context.Background())\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Println(\"failed to get JWT payload as map:\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tpayloadJSON, err := json.MarshalIndent(rawTokenMap, \"\", \"  \")\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Println(\"failed to marshal JWT payload as JSON:\", err)\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"JWT payload: %s\\n\", string(payloadJSON))\n\t\t\t}\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", \"\", \"config file\")\n\tcmd.Flags().BoolVar(&pretty, \"pretty\", true, \"pretty print the JWT payload\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/jwt/root.go",
    "content": "package jwt\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewJwtCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"jwt\",\n\t\tShort: \"Tools for handling JSON Web Tokens\",\n\t\tLong:  ``,\n\t}\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := NewJwtCmd()\n\tparent.AddCommand(cmd)\n\tcmd.AddCommand(NewCreateCommand())\n}\n"
  },
  {
    "path": "backend/cmd/migrate/down.go",
    "content": "package migrate\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"log\"\n\t\"strconv\"\n)\n\nvar steps int\n\nfunc NewMigrateDownCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"down\",\n\t\tShort: \"migrate the database down - given the number of steps\",\n\t\tLong:  ``,\n\t\tArgs: func(cmd *cobra.Command, args []string) error {\n\t\t\tvar err error\n\t\t\tif steps, err = strconv.Atoi(args[0]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tfmt.Println(\"migrate down called\")\n\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\terr = persister.MigrateDown(steps)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/migrate/root.go",
    "content": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n\n*/\n\npackage migrate\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\n//var persister *persistence.Persister\n\nfunc NewMigrateCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"migrate\",\n\t\tShort: \"Database migration helpers\",\n\t\tLong:  ``,\n\t}\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := NewMigrateCmd()\n\tparent.AddCommand(cmd)\n\tcmd.AddCommand(NewMigrateUpCommand())\n\tcmd.AddCommand(NewMigrateDownCommand())\n}\n"
  },
  {
    "path": "backend/cmd/migrate/up.go",
    "content": "package migrate\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"log\"\n)\n\nfunc NewMigrateUpCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"up\",\n\t\tShort: \"migrate the database up\",\n\t\tLong:  ``,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tlog.Println(\"migrate up\")\n\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\terr = persister.MigrateUp()\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\terr = persister.GetConnection().Close()\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(fmt.Errorf(\"failed to close db connection: %w\", err))\n\t\t\t}\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/root.go",
    "content": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage cmd\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/cleanup\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/isready\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/jwk\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/jwt\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/migrate\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/schema\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/serve\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/siwa\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/user\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/version\"\n\t\"log\"\n)\n\nfunc NewRootCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse: \"hanko\",\n\t}\n\n\tmigrate.RegisterCommands(cmd)\n\tserve.RegisterCommands(cmd)\n\tisready.RegisterCommands(cmd)\n\tjwk.RegisterCommands(cmd)\n\tjwt.RegisterCommands(cmd)\n\tversion.RegisterCommands(cmd)\n\tuser.RegisterCommands(cmd)\n\tsiwa.RegisterCommands(cmd)\n\tschema.RegisterCommands(cmd)\n\tcleanup.RegisterCommands(cmd)\n\n\treturn cmd\n}\n\n// Execute adds all child commands to the root command and sets flags appropriately.\n// This is called by main.main(). It only needs to happen once to the rootCmd.\nfunc Execute() {\n\tcmd := NewRootCmd()\n\n\terr := cmd.Execute()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "backend/cmd/schema/generate.go",
    "content": "package schema\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/invopop/jsonschema\"\n\t\"github.com/spf13/cobra\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\nfunc NewGenerateCommand() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"generate\",\n\t\tShort: \"Generate JSON Schema\",\n\t\tLong:  ``,\n\t}\n\n\tcmd.AddCommand(NewGenerateImportCommand())\n\tcmd.AddCommand(NewGenerateConfigCommand())\n\n\treturn cmd\n}\n\ntype generateSchemaParams struct {\n\tstructure       interface{}\n\trootSchemaTitle string\n\toutput          string\n\tdoNotReference  bool\n\textractComments bool\n\tcommentPaths    []string\n}\n\nfunc generateSchema(params generateSchemaParams) error {\n\tr := new(jsonschema.Reflector)\n\n\tif params.doNotReference {\n\t\tr.DoNotReference = true\n\t}\n\n\tif params.extractComments {\n\t\tfor _, path := range params.commentPaths {\n\t\t\tif err := r.AddGoComments(\"github.com/teamhanko/hanko/backend/v2\", path); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\ts := r.Reflect(params.structure)\n\n\tif params.rootSchemaTitle != \"\" {\n\t\ts.Title = params.rootSchemaTitle\n\t}\n\n\tdata, err := json.MarshalIndent(s, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\toutPath, outFile := filepath.Split(params.output)\n\n\tif outPath != \"\" {\n\t\t_, err = os.Stat(outPath)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t\tlog.Printf(\"directory %s does not exist, creating directory\", outPath)\n\n\t\t\t\tmkDirErr := os.MkdirAll(outPath, 0750)\n\n\t\t\t\tif mkDirErr != nil {\n\t\t\t\t\treturn fmt.Errorf(\"could not create directory %s: %w\", outPath, mkDirErr)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"could not get file info: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar destination string\n\tif outFile == \"\" {\n\t\tlog.Println(\"no output file given, using default: output.schema.json\")\n\t\tdestination, err = filepath.Abs(filepath.Join(outPath, \"output.schema.json\"))\n\t} else {\n\t\tdestination, err = filepath.Abs(filepath.Join(outPath, outFile))\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not determine file destination: %w\", err)\n\t}\n\n\terr = os.WriteFile(destination, data, 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not write file to destination %s: %w\", destination, err)\n\t}\n\n\tlog.Printf(\"schema generated successfully at: %s\\n\", destination)\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/cmd/schema/generate_config.go",
    "content": "package schema\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n)\n\nfunc NewGenerateConfigCommand() *cobra.Command {\n\tvar (\n\t\toutput string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"config\",\n\t\tShort: \"Generate JSON schema for the backend config\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\terr := generateSchema(generateSchemaParams{\n\t\t\t\tstructure:       &config.Config{},\n\t\t\t\trootSchemaTitle: \"Config\",\n\t\t\t\toutput:          output,\n\t\t\t\textractComments: true,\n\t\t\t\tdoNotReference:  false,\n\t\t\t\tcommentPaths:    []string{\"config\", \"ee\"},\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(\n\t\t&output, \"output\",\n\t\t\"o\",\n\t\t\"json_schema/hanko.config.json\",\n\t\t\"Output file\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/schema/generate_import.go",
    "content": "package schema\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/user\"\n\t\"log\"\n)\n\nfunc NewGenerateImportCommand() *cobra.Command {\n\tvar (\n\t\toutput string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"import\",\n\t\tShort: \"Generate JSON schema for the user import/export\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\terr := generateSchema(generateSchemaParams{\n\t\t\t\tstructure:       &user.ImportOrExportList{},\n\t\t\t\trootSchemaTitle: \"User import\",\n\t\t\t\toutput:          output,\n\t\t\t\textractComments: true,\n\t\t\t\tdoNotReference:  false,\n\t\t\t\tcommentPaths:    []string{\"cmd/user\"},\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(\n\t\t&output, \"output\",\n\t\t\"o\",\n\t\t\"json_schema/hanko.user_import.json\",\n\t\t\"Output file\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/schema/markdown.go",
    "content": "package schema\n\nimport \"github.com/spf13/cobra\"\n\nfunc NewMarkdownCommand() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"markdown\",\n\t\tShort: \"Generate markdown from JSON Schema\",\n\t\tLong:  ``,\n\t}\n\n\tcmd.AddCommand(NewMarkdownConfigCommand())\n\tcmd.AddCommand(NewMarkdownImportCommand())\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/schema/markdown_config.go",
    "content": "package schema\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n\t\"os/exec\"\n\t\"path/filepath\"\n)\n\nfunc NewMarkdownConfigCommand() *cobra.Command {\n\tvar (\n\t\toutPath string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"config\",\n\t\tShort: \"Generate markdown for the backend config\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\terr := generateSchema(generateSchemaParams{\n\t\t\t\tstructure:       &config.Config{},\n\t\t\t\trootSchemaTitle: \"Config\",\n\t\t\t\toutput:          filepath.Join(outPath, \"config.schema.json\"),\n\t\t\t\textractComments: true,\n\t\t\t\tdoNotReference:  true,\n\t\t\t\tcommentPaths:    []string{\"config\", \"ee\"},\n\t\t\t})\n\n\t\t\tout, err := exec.Command(\"npx\",\n\t\t\t\t\"@adobe/jsonschema2md\",\n\t\t\t\tfmt.Sprintf(\"--input=%s\", outPath),\n\t\t\t\tfmt.Sprintf(\"--out=%s\", outPath),\n\t\t\t\t\"--schema-extension=schema.json\",\n\t\t\t\t\"--example-format=yaml\",\n\t\t\t\t\"--header=false\",\n\t\t\t\t\"--skip=definedinfact\",\n\t\t\t\t\"--skip=typefact\",\n\t\t\t\t\"--schema-out=-\",\n\t\t\t\t\"--properties=format\",\n\t\t\t\t\"--no-readme=true\").\n\t\t\t\tCombinedOutput()\n\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tlog.Println(string(out))\n\n\t\t\toutPathAbs, _ := filepath.Abs(outPath)\n\t\t\tlog.Printf(\"successfully generated markdown from JSON schema at: %s\\n\", outPathAbs)\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(\n\t\t&outPath, \"out-path\",\n\t\t\"o\",\n\t\t\".generated/docs/config\",\n\t\t\"Output path\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/schema/markdown_import.go",
    "content": "package schema\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/user\"\n\t\"log\"\n\t\"os/exec\"\n\t\"path/filepath\"\n)\n\nfunc NewMarkdownImportCommand() *cobra.Command {\n\tvar (\n\t\toutPath string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"import\",\n\t\tShort: \"Generate markdown for the user import schema\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\terr := generateSchema(generateSchemaParams{\n\t\t\t\tstructure:       &user.ImportOrExportList{},\n\t\t\t\trootSchemaTitle: \"User import\",\n\t\t\t\toutput:          filepath.Join(outPath, \"user_import.schema.json\"),\n\t\t\t\textractComments: true,\n\t\t\t\tdoNotReference:  true,\n\t\t\t\tcommentPaths:    []string{\"cmd/user\"},\n\t\t\t})\n\n\t\t\tout, err := exec.Command(\"npx\",\n\t\t\t\t\"@adobe/jsonschema2md\",\n\t\t\t\tfmt.Sprintf(\"--input=%s\", outPath),\n\t\t\t\tfmt.Sprintf(\"--out=%s\", outPath),\n\t\t\t\t\"--schema-extension=schema.json\",\n\t\t\t\t\"--example-format=yaml\",\n\t\t\t\t\"--header=false\",\n\t\t\t\t\"--skip=definedinfact\",\n\t\t\t\t\"--skip=typefact\",\n\t\t\t\t\"--schema-out=-\",\n\t\t\t\t\"--properties=format\",\n\t\t\t\t\"--no-readme=true\").\n\t\t\t\tCombinedOutput()\n\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tlog.Println(string(out))\n\n\t\t\toutPathAbs, _ := filepath.Abs(outPath)\n\t\t\tlog.Printf(\"successfully generated markdown from JSON schema at: %s\\n\", outPathAbs)\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(\n\t\t&outPath, \"out-path\",\n\t\t\"o\",\n\t\t\".generated/docs/import\",\n\t\t\"Output path\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/schema/root.go",
    "content": "package schema\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewSchemaCommand() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"schema\",\n\t\tShort: \"JSONSchema related commands\",\n\t\tLong:  ``,\n\t}\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := NewSchemaCommand()\n\tcmd.AddCommand(NewGenerateCommand())\n\tcmd.AddCommand(NewMarkdownCommand())\n\tparent.AddCommand(cmd)\n}\n"
  },
  {
    "path": "backend/cmd/serve/admin.go",
    "content": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage serve\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/server\"\n\t\"log\"\n\t\"sync\"\n)\n\nfunc NewServeAdminCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"admin\",\n\t\tShort: \"Start the admin portion of the hanko server\",\n\t\tLong:  ``,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(1)\n\n\t\t\tgo server.StartAdmin(cfg, &wg, persister, nil)\n\n\t\t\twg.Wait()\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/serve/all.go",
    "content": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage serve\n\nimport (\n\t\"github.com/labstack/echo-contrib/echoprometheus\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/server\"\n\t\"log\"\n\t\"sync\"\n)\n\nfunc NewServeAllCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile                string\n\t\tauthenticatorMetadataFile string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"all\",\n\t\tShort: \"Start the public and admin portion of the hanko server\",\n\t\tLong:  ``,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tauthenticatorMetadata := mapper.LoadAuthenticatorMetadata(&authenticatorMetadataFile)\n\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(2)\n\n\t\t\tprometheus := echoprometheus.NewMiddleware(\"hanko\")\n\n\t\t\tgo server.StartPublic(cfg, &wg, persister, prometheus, authenticatorMetadata)\n\t\t\tgo server.StartAdmin(cfg, &wg, persister, prometheus)\n\n\t\t\twg.Wait()\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\tcmd.Flags().StringVar(&authenticatorMetadataFile, \"auth-meta\", \"\", \"authenticator metadata file\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/serve/public.go",
    "content": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage serve\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/server\"\n\t\"log\"\n\t\"sync\"\n)\n\nfunc NewServePublicCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile                string\n\t\tauthenticatorMetadataFile string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"public\",\n\t\tShort: \"Start the public portion of the hanko server\",\n\t\tLong:  ``,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tauthenticatorMetadata := mapper.LoadAuthenticatorMetadata(&authenticatorMetadataFile)\n\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(1)\n\n\t\t\tgo server.StartPublic(cfg, &wg, persister, nil, authenticatorMetadata)\n\n\t\t\twg.Wait()\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\tcmd.Flags().StringVar(&authenticatorMetadataFile, \"auth-meta\", \"\", \"authenticator metadata file\")\n\n\treturn cmd\n}\n"
  },
  {
    "path": "backend/cmd/serve/root.go",
    "content": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n\n*/\npackage serve\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewServeCommand() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"serve\",\n\t\tShort: \"Start the hanko server\",\n\t\tLong:  ``,\n\t}\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := NewServeCommand()\n\tparent.AddCommand(cmd)\n\tcmd.AddCommand(NewServePublicCommand())\n\tcmd.AddCommand(NewServeAdminCommand())\n\tcmd.AddCommand(NewServeAllCommand())\n}\n"
  },
  {
    "path": "backend/cmd/siwa/siwa.go",
    "content": "package siwa\n\nimport (\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jws\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/spf13/cobra\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"path/filepath\"\n\t\"time\"\n)\n\nfunc NewSignInWithAppleCommand() *cobra.Command {\n\tvar (\n\t\tprivateKey string\n\t\tteamID     string\n\t\tservicesID string\n\t\tkeyID      string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"siwa\",\n\t\tShort: \"Generate a client secret (JWT) for Sign in with Apple (SIWA)\",\n\t\tLong: `Sign in with Apple requires JWTs to authorize requests. This command creates the token,\nthen signs it with the private key obtained from the Apple Developer console.\n\nSee: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens#3262048`,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tpath := filepath.Clean(privateKey)\n\t\t\tbytes, err := ioutil.ReadFile(path)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tblock, _ := pem.Decode(bytes)\n\t\t\tkey, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tnow := time.Now().UTC()\n\t\t\tt := jwt.New()\n\t\t\t_ = t.Set(jwt.SubjectKey, servicesID)\n\t\t\t_ = t.Set(jwt.IssuerKey, teamID)\n\t\t\t_ = t.Set(jwt.IssuedAtKey, now.Unix())\n\t\t\t_ = t.Set(jwt.AudienceKey, \"https://appleid.apple.com\")\n\t\t\t_ = t.Set(jwt.ExpirationKey, now.Add(time.Hour*24*180).Unix())\n\n\t\t\theaders := jws.NewHeaders()\n\t\t\t_ = headers.Set(jws.KeyIDKey, keyID)\n\n\t\t\tsigned, err := jwt.Sign(t, jwt.WithKey(jwa.ES256, key, jws.WithProtectedHeaders(headers)))\n\t\t\tfmt.Println(string(signed))\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(&privateKey, \"private_key\", \"p\", \"\", \"Path to the private key file\")\n\tcmd.Flags().StringVarP(&teamID, \"team_id\", \"t\", \"\", \"Apple team ID\")\n\tcmd.Flags().StringVarP(&servicesID, \"services_id\", \"s\", \"\", \"Apple services ID\")\n\tcmd.Flags().StringVarP(&keyID, \"key_id\", \"k\", \"\", \"Apple key ID\")\n\n\t_ = cmd.MarkFlagRequired(\"private_key\")\n\t_ = cmd.MarkFlagRequired(\"team_id\")\n\t_ = cmd.MarkFlagRequired(\"services_id\")\n\t_ = cmd.MarkFlagRequired(\"key_id\")\n\n\treturn cmd\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tparent.AddCommand(NewSignInWithAppleCommand())\n}\n"
  },
  {
    "path": "backend/cmd/user/export.go",
    "content": "package user\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n)\n\nfunc NewExportCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile string\n\t\toutputFile string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"export\",\n\t\tShort: \"Export users from database into a Json file\",\n\t\tLong:  ``,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\terr = export(persister, outputFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tlog.Println(fmt.Sprintf(\"Successfully exported users to %s\", outputFile))\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\tcmd.Flags().StringVarP(&outputFile, \"outputFile\", \"o\", \"\", \"The path of the output file.\")\n\terr := cmd.MarkFlagRequired(\"outputFile\")\n\tif err != nil {\n\t\tlog.Println(err)\n\t}\n\treturn cmd\n}\n\nfunc export(persister persistence.Persister, outFile string) error {\n\tvar entries []ImportOrExportEntry\n\tusers, err := persister.GetUserPersister().All()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get list of users: %w\", err)\n\t}\n\tfor _, user := range users {\n\t\tvar emails []ImportOrExportEmail\n\t\tfor _, email := range user.Emails {\n\t\t\temails = append(emails, ImportOrExportEmail{\n\t\t\t\tAddress:    email.Address,\n\t\t\t\tIsPrimary:  email.IsPrimary(),\n\t\t\t\tIsVerified: email.Verified,\n\t\t\t})\n\t\t}\n\t\tentry := ImportOrExportEntry{\n\t\t\tUserID:    user.ID.String(),\n\t\t\tEmails:    emails,\n\t\t\tCreatedAt: &user.CreatedAt,\n\t\t\tUpdatedAt: &user.UpdatedAt,\n\t\t}\n\t\tentries = append(entries, entry)\n\t}\n\tbytes, err := json.Marshal(entries)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(outFile, bytes, 0600)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/cmd/user/format.go",
    "content": "package user\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/invopop/jsonschema\"\n\t\"time\"\n)\n\n// ImportOrExportEmail The import/export format for a user's email\ntype ImportOrExportEmail struct {\n\t// Address Valid email address\n\tAddress string `json:\"address\" yaml:\"address\" jsonschema:\"format=email\" validate:\"email\"`\n\t// IsPrimary indicates if this is the primary email of the users. In the Emails array there has to be exactly one primary email.\n\tIsPrimary bool `json:\"is_primary\" yaml:\"is_primary\"`\n\t// IsVerified indicates if the email address was previously verified.\n\tIsVerified bool `json:\"is_verified\" yaml:\"is_verified\"`\n}\n\nfunc (ImportOrExportEmail) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tschema.Title = \"ImportEmail\"\n}\n\n// Emails Array of email addresses\ntype Emails []ImportOrExportEmail\n\ntype ImportWebauthnCredential struct {\n\t// ID of the WebAuthn credential.\n\tID string `json:\"id\" yaml:\"id\" validate:\"required\"`\n\t// Optional Name of the WebAuthn credential.\n\tName *string `json:\"name\" yaml:\"name\" validate:\"omitempty\"`\n\t// The PublicKey of the credential.\n\tPublicKey string `json:\"public_key\" yaml:\"public_key\" validate:\"required\"`\n\t// The AttestationType the credential was created with.\n\tAttestationType string `json:\"attestation_type\" yaml:\"attestation_type\" validate:\"required\"`\n\t// Optional AAGUID of the authenticator on which the credential was created on.\n\tAAGUID uuid.UUID `json:\"aaguid\" yaml:\"aaguid\" validate:\"omitempty,uuid4\"`\n\t// Optional SignCount of the WebAuthn credential.\n\tSignCount int `json:\"sign_count\" yaml:\"sign_count\"`\n\t// LastUsedAt optional timestamp when the WebAuthn credential was last used.\n\tLastUsedAt *time.Time `json:\"last_used_at\" yaml:\"last_used_at\" validate:\"omitempty\"`\n\t// CreatedAt optional timestamp of the WebAuthn credentials' creation. Will be set to the import date if not provided.\n\tCreatedAt *time.Time `json:\"created_at\" yaml:\"created_at\" validate:\"omitempty\"`\n\t// UpdatedAt optional timestamp of the last update to the WebAuthn credential. Will be set to the import date if not provided.\n\tUpdatedAt *time.Time `json:\"updated_at\" yaml:\"updated_at\" validate:\"omitempty\"`\n\t// Optional list of supported Transports by the authenticator.\n\tTransports []string `json:\"transports\" yaml:\"transports\" validate:\"omitempty,unique\"`\n\t// BackupEligible flag indicates if the WebAuthn credential can be backed up (e.g. in Apple KeyChain, ...). If the information is not available set it to false.\n\tBackupEligible bool `json:\"backup_eligible\" yaml:\"backup_eligible\"`\n\t// BackupState flag indicates if the WebAuthn credential is backed up (e.g. in Apple KeyChain, ...). If the information is not available set it to false.\n\tBackupState bool `json:\"backup_state\" yaml:\"backup_state\"`\n\t// MFAOnly flag indicates if the WebAuthn credential can only be used in combination with another login factor (e.g. password, ...).\n\tMFAOnly bool `json:\"mfa_only\" yaml:\"mfa_only\"`\n\t// UserHandle optional user id which was used to create the credential with.\n\t// Populate only when user id was not an uuid v4 and the WebAuthn credential is not an MFAOnly credential.\n\tUserHandle *string `json:\"user_handle\" yaml:\"user_handle\" validate:\"omitempty,excluded_if=MFAOnly true\"`\n}\n\ntype ImportWebauthnCredentials []ImportWebauthnCredential\n\ntype ImportPasswordCredential struct {\n\t// Password hash of the password in bcrypt format.\n\tPassword string `json:\"password\" yaml:\"password\" validate:\"required,startswith=$2a$\"`\n\t// CreatedAt optional timestamp when the password was created. Will be set to the import date if not provided.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\" yaml:\"created_at\" validate:\"omitempty\"`\n\t// UpdatedAt optional timestamp of the last update to the password. Will be set to the import date if not provided.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\" yaml:\"updated_at\" validate:\"omitempty\"`\n}\n\ntype ImportOTPSecret struct {\n\t// Secret of the TOTP credential. TOTP credential must be generated for a period of 30 seconds and SHA1 hash algorithm.\n\tSecret string `json:\"secret\" yaml:\"secret\" validate:\"required\"`\n\t// CreatedAt optional timestamp when the otp secret was created. Will be set to the import date if not provided.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\" yaml:\"created_at\" validate:\"omitempty\"`\n\t// UpdatedAt optional timestamp of the last update to the otp secret. Will be set to the import date if not provided.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\" yaml:\"updated_at\" validate:\"omitempty\"`\n}\n\n// ImportOrExportEntry represents a user to be imported/export to the Hanko database\ntype ImportOrExportEntry struct {\n\t// UserID optional uuid.v4. If not provided a new one will be generated for the user\n\tUserID string `json:\"user_id,omitempty\" yaml:\"user_id\" validate:\"omitempty,uuid\"`\n\t// Emails optional list of emails\n\tEmails Emails `json:\"emails\" yaml:\"emails\" jsonschema:\"type=array,minItems=1\" validate:\"required_if=Username 0,unique=Address,dive\"`\n\t// Username optional username of the user\n\tUsername *string `json:\"username,omitempty\" yaml:\"username\" validate:\"required_if=Emails 0,omitempty,gte=1\"`\n\t// WebauthnCredentials optional list of WebAuthn credentials of a user. Includes passkeys and MFA credentials.\n\tWebauthnCredentials ImportWebauthnCredentials `json:\"webauthn_credentials,omitempty\" yaml:\"webauthn_credentials\" validate:\"omitempty,unique=ID,dive\"`\n\t// Password optional password.\n\tPassword *ImportPasswordCredential `json:\"password\" yaml:\"password\" validate:\"omitempty\"`\n\t// OTPSecret optional TOTP secret for MFA.\n\tOTPSecret *ImportOTPSecret `json:\"otp_secret\" yaml:\"otp_secret\" validate:\"omitempty\"`\n\t// CreatedAt optional timestamp of the users' creation. Will be set to the import date if not provided.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\" yaml:\"created_at\" validate:\"omitempty\"`\n\t// UpdatedAt optional timestamp of the last update to the user. Will be set to the import date if not provided.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\" yaml:\"updated_at\" validate:\"omitempty\"`\n}\n\nfunc (ImportOrExportEntry) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tschema.Title = \"ImportEntry\"\n}\n\n// ImportOrExportList a list of ImportEntries\ntype ImportOrExportList []ImportOrExportEntry\n\nfunc (ImportOrExportList) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tdate := time.Date(2024, 8, 17, 12, 5, 15, 651387237, time.UTC)\n\tusername := \"example\"\n\tschema.Examples = []any{\n\t\t[]ImportOrExportEntry{\n\t\t\t{\n\t\t\t\tUserID: \"a9ae6bc8-d829-43de-b672-f50230833877\",\n\t\t\t\tEmails: Emails{\n\t\t\t\t\t{\"test@example.com\", true, true},\n\t\t\t\t\t{\"test+1@example.com\", false, false},\n\t\t\t\t},\n\t\t\t\tCreatedAt: &date,\n\t\t\t\tUpdatedAt: &date,\n\t\t\t},\n\t\t\t{\n\t\t\t\tUserID: \"2f0649cf-c71e-48a5-92c3-210addb80281\",\n\t\t\t\tEmails: Emails{\n\t\t\t\t\t{\"test2@example.com\", true, true},\n\t\t\t\t\t{\"test2+1@example.com\", false, false},\n\t\t\t\t},\n\t\t\t\tCreatedAt: &date,\n\t\t\t\tUpdatedAt: &date,\n\t\t\t},\n\t\t},\n\t\t[]ImportOrExportEntry{\n\t\t\t{\n\t\t\t\tUsername: &username,\n\t\t\t\tPassword: &ImportPasswordCredential{\n\t\t\t\t\tPassword:  \"$2a$12$mFbud0mLsD/q.WG7/9pNQemlAHs3H4o8zAv44gsUF1v1awsdqTh7.\",\n\t\t\t\t\tCreatedAt: &date,\n\t\t\t\t\tUpdatedAt: &date,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (entry *ImportOrExportEntry) validate(v *validator.Validate) error {\n\terr := v.Struct(entry)\n\tif err != nil {\n\t\treturn err\n\t}\n\tprimaryEmailAddresses := 0\n\tfor _, email := range entry.Emails {\n\t\tif email.IsPrimary {\n\t\t\tprimaryEmailAddresses++\n\t\t}\n\t}\n\n\tif len(entry.Emails) > 0 && primaryEmailAddresses != 1 {\n\t\treturn errors.New(fmt.Sprintf(\"Need exactly one primary email, got %v\", primaryEmailAddresses))\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/cmd/user/format_test.go",
    "content": "package user\n\nimport (\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/gofrs/uuid\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst validUUID = \"62418053-a2cd-47a8-9b61-4426380d263a\"\nconst invalidUUID = \"notvalid\"\n\nfunc TestImportEntry_validate(t *testing.T) {\n\tv := validator.New()\n\tvalidUsername := \"example\"\n\temptyUsername := \"\"\n\ttype fields struct {\n\t\tUserID              string\n\t\tEmails              Emails\n\t\tUsername            *string\n\t\tWebauthnCredentials ImportWebauthnCredentials\n\t\tPassword            *ImportPasswordCredential\n\t\tOTPSecret           *ImportOTPSecret\n\t\tCreatedAt           *time.Time\n\t\tUpdatedAt           *time.Time\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr assert.ErrorAssertionFunc\n\t}{\n\t\t{\n\t\t\tname: \"User with one primary email must validate\",\n\t\t\tfields: fields{\n\t\t\t\tUserID: \"\",\n\t\t\t\tEmails: Emails{\n\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tCreatedAt: nil,\n\t\t\t\tUpdatedAt: nil,\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"User with no email but username must validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"UserID with valid uuid must validate\",\n\t\t\tfields: fields{\n\t\t\t\tUserID: validUUID,\n\t\t\t\tEmails: Emails{\n\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tCreatedAt: nil,\n\t\t\t\tUpdatedAt: nil,\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"UserID with invalid uuid must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUserID: invalidUUID,\n\t\t\t\tEmails: Emails{\n\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tCreatedAt: nil,\n\t\t\t\tUpdatedAt: nil,\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"User with no email and username must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUserID:    \"\",\n\t\t\t\tEmails:    nil,\n\t\t\t\tCreatedAt: nil,\n\t\t\t\tUpdatedAt: nil,\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"User with an empty username must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tEmails: Emails{\n\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tUsername: &emptyUsername,\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"User with no primary email must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUserID: \"\",\n\t\t\t\tEmails: Emails{\n\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\tIsPrimary:  false,\n\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tCreatedAt: nil,\n\t\t\t\tUpdatedAt: nil,\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"More than one Primary must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUserID: \"\",\n\t\t\t\tEmails: Emails{\n\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t},\n\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\tAddress:    \"primary2@hanko.io\",\n\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tCreatedAt: nil,\n\t\t\t\tUpdatedAt: nil,\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"Valid webauthn credential must validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tWebauthnCredentials: ImportWebauthnCredentials{\n\t\t\t\t\twebauthnCredential,\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"Webauthn credential without id must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tWebauthnCredentials: ImportWebauthnCredentials{\n\t\t\t\t\twebauthnCredentialWithEmptyID,\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"Webauthn credential without public key must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tWebauthnCredentials: ImportWebauthnCredentials{\n\t\t\t\t\twebauthnCredentialWithEmptyPublicKey,\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"Webauthn credential without attestation type must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tWebauthnCredentials: ImportWebauthnCredentials{\n\t\t\t\t\twebauthnCredentialWithEmptyAttestationType,\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"User with password must validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tPassword: &ImportPasswordCredential{\n\t\t\t\t\tPassword: \"$2a$12$mFbud0mLsD/q.WG7/9pNQemlAHs3H4o8zAv44gsUF1v1awsdqTh7.\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"User with empty password string must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tPassword: &ImportPasswordCredential{\n\t\t\t\t\tPassword: \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"Wrong formatted password must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tPassword: &ImportPasswordCredential{\n\t\t\t\t\tPassword: \"$12$mFbud0mLsD/q.WG7/9pNQemlAHs3H4o8zAv44gsUF1v1awsdqTh7.\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"User with OTPSecret must validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tOTPSecret: &ImportOTPSecret{\n\t\t\t\t\tSecret: \"MYOTPSECRET\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"User with empty OTPSecret string must not validate\",\n\t\t\tfields: fields{\n\t\t\t\tUsername: &validUsername,\n\t\t\t\tOTPSecret: &ImportOTPSecret{\n\t\t\t\t\tSecret: \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tentry := &ImportOrExportEntry{\n\t\t\t\tUserID:              tt.fields.UserID,\n\t\t\t\tEmails:              tt.fields.Emails,\n\t\t\t\tCreatedAt:           tt.fields.CreatedAt,\n\t\t\t\tUpdatedAt:           tt.fields.UpdatedAt,\n\t\t\t\tUsername:            tt.fields.Username,\n\t\t\t\tWebauthnCredentials: tt.fields.WebauthnCredentials,\n\t\t\t\tPassword:            tt.fields.Password,\n\t\t\t\tOTPSecret:           tt.fields.OTPSecret,\n\t\t\t}\n\t\t\ttt.wantErr(t, entry.validate(v), fmt.Sprintf(\"validate()\"))\n\t\t})\n\t}\n}\n\nvar webauthnCredential = ImportWebauthnCredential{\n\tID:              \"randomID\",\n\tName:            nil,\n\tPublicKey:       \"randomPublicKey\",\n\tAttestationType: \"none\",\n\tAAGUID:          uuid.Nil,\n\tSignCount:       0,\n\tLastUsedAt:      nil,\n\tCreatedAt:       nil,\n\tUpdatedAt:       nil,\n\tTransports:      nil,\n\tBackupEligible:  false,\n\tBackupState:     false,\n\tMFAOnly:         false,\n}\n\nvar webauthnCredentialWithEmptyID = ImportWebauthnCredential{\n\tID:              \"\",\n\tName:            nil,\n\tPublicKey:       \"randomPublicKey\",\n\tAttestationType: \"none\",\n\tAAGUID:          uuid.Nil,\n\tSignCount:       0,\n\tLastUsedAt:      nil,\n\tCreatedAt:       nil,\n\tUpdatedAt:       nil,\n\tTransports:      nil,\n\tBackupEligible:  false,\n\tBackupState:     false,\n\tMFAOnly:         false,\n}\n\nvar webauthnCredentialWithEmptyPublicKey = ImportWebauthnCredential{\n\tID:              \"randomID\",\n\tName:            nil,\n\tPublicKey:       \"\",\n\tAttestationType: \"none\",\n\tAAGUID:          uuid.Nil,\n\tSignCount:       0,\n\tLastUsedAt:      nil,\n\tCreatedAt:       nil,\n\tUpdatedAt:       nil,\n\tTransports:      nil,\n\tBackupEligible:  false,\n\tBackupState:     false,\n\tMFAOnly:         false,\n}\n\nvar webauthnCredentialWithEmptyAttestationType = ImportWebauthnCredential{\n\tID:              \"randomID\",\n\tName:            nil,\n\tPublicKey:       \"randomPublicKey\",\n\tAttestationType: \"\",\n\tAAGUID:          uuid.Nil,\n\tSignCount:       0,\n\tLastUsedAt:      nil,\n\tCreatedAt:       nil,\n\tUpdatedAt:       nil,\n\tTransports:      nil,\n\tBackupEligible:  false,\n\tBackupState:     false,\n\tMFAOnly:         false,\n}\n"
  },
  {
    "path": "backend/cmd/user/generate.go",
    "content": "package user\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/brianvoe/gofakeit/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar outputFile string\nvar count int\n\nfunc NewGenerateCommand() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"generate\",\n\t\tShort: \"Generate mock users and write them to a file.\",\n\t\tLong:  ``,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\terr := generate()\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t}\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(&outputFile, \"outputFile\", \"o\", \"\", \"The path of the output file.\")\n\terr := cmd.MarkFlagRequired(\"outputFile\")\n\tif err != nil {\n\t\tlog.Println(err)\n\t}\n\tcmd.Flags().IntVarP(&count, \"count\", \"c\", 10, \"Gives the number of users that should be generated.\")\n\treturn cmd\n}\n\nfunc generate() error {\n\tvar entries []ImportOrExportEntry\n\tfor i := 0; i < count; i++ {\n\t\tnow := time.Now().UTC()\n\t\tid, _ := uuid.NewV4()\n\t\temails := []ImportOrExportEmail{\n\t\t\t{\n\t\t\t\tAddress:    gofakeit.Email(),\n\t\t\t\tIsPrimary:  true,\n\t\t\t\tIsVerified: true,\n\t\t\t},\n\t\t}\n\t\tentry := ImportOrExportEntry{\n\t\t\tUserID:    id.String(),\n\t\t\tEmails:    emails,\n\t\t\tCreatedAt: &now,\n\t\t\tUpdatedAt: &now,\n\t\t}\n\t\tentries = append(entries, entry)\n\t}\n\tbytes, err := json.Marshal(entries)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(outputFile, bytes, 0600)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/cmd/user/import.go",
    "content": "package user\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n)\n\nfunc NewImportCommand() *cobra.Command {\n\tvar (\n\t\tconfigFile string\n\t\tinputFile  string\n\t\tinputUrl   string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"import\",\n\t\tShort: \"Import users into database from a Json file\",\n\t\tLong:  ``,\n\t\tPreRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tfileFlagSet := cmd.Flags().Changed(\"inputFile\")\n\t\t\turlFlagSet := cmd.Flags().Changed(\"inputUrl\")\n\t\t\tif !fileFlagSet && !urlFlagSet {\n\t\t\t\treturn errors.New(\"either flag \\\"inputFile\\\" or \\\"inputUrl\\\" must be set\")\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\t//Load cfg\n\t\t\tcfg, err := config.Load(&configFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\n\t\t\tvar reader io.ReadCloser\n\t\t\tfileAvailable := cmd.Flags().Changed(\"inputFile\")\n\t\t\turlAvailable := cmd.Flags().Changed(\"inputUrl\")\n\n\t\t\tif fileAvailable {\n\t\t\t\t//Load File\n\t\t\t\t// we explicitly want user input here, hence  #nosec G304\n\t\t\t\treader, err = os.Open(inputFile)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Fatal(err)\n\t\t\t\t}\n\t\t\t} else if urlAvailable {\n\t\t\t\t// Load file from url\n\t\t\t\tresponse, err := http.Get(inputUrl)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\tif response.StatusCode < 200 || response.StatusCode > 299 {\n\t\t\t\t\tlog.Fatal(fmt.Errorf(\"failed to get file from url: %s\", response.Status))\n\t\t\t\t}\n\n\t\t\t\treader = response.Body\n\t\t\t}\n\n\t\t\tdefer func() {\n\t\t\t\tif reader != nil {\n\t\t\t\t\tif err := reader.Close(); err != nil {\n\t\t\t\t\t\tlog.Printf(\"Error closing file: %s\\n\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tusers, err := loadAndValidate(reader)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\t//Import Users\n\t\t\tpersister, err := persistence.New(cfg.Database)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\terr = addToDatabase(users, persister)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tlog.Println(fmt.Sprintf(\"Successfully imported %v users.\", len(users)))\n\t\t},\n\t}\n\n\tcmd.Flags().StringVar(&configFile, \"config\", config.DefaultConfigFilePath, \"config file\")\n\tcmd.Flags().StringVarP(&inputFile, \"inputFile\", \"i\", \"\", \"The json file where the users should be imported from.\")\n\tcmd.Flags().StringVarP(&inputUrl, \"inputUrl\", \"u\", \"\", \"The url to a json file where the users should be imported from.\")\n\tcmd.MarkFlagsMutuallyExclusive(\"inputFile\", \"inputUrl\")\n\treturn cmd\n}\n\n// loadAndValidate reads json from an io.Reader so we read every entry separate and validate it. We go through the whole\n// array to print out every validation error in the input data.\nfunc loadAndValidate(input io.Reader) ([]ImportOrExportEntry, error) {\n\tdec := json.NewDecoder(input)\n\n\t// read the open bracket\n\t_, err := dec.Token()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tusers := []ImportOrExportEntry{}\n\tv := validator.New()\n\n\tnumErrors := 0\n\tindex := 0\n\t// while the array contains values\n\tfor dec.More() {\n\t\tindex = index + 1\n\t\tvar userEntry ImportOrExportEntry\n\t\t// decode one ImportEntry\n\t\terr := dec.Decode(&userEntry)\n\t\tif err != nil {\n\t\t\terrorMsg := fmt.Sprintf(\"Error at entry %v : %v\", index, err.Error())\n\t\t\tlog.Println(errorMsg)\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := userEntry.validate(v); err != nil {\n\t\t\tvErrs := dto.TransformValidationErrors(err)\n\t\t\terrorMsg := fmt.Sprintf(\"Error at entry %v : %v\", index, strings.Join(vErrs, \" and \"))\n\t\t\tlog.Println(errorMsg)\n\t\t\tnumErrors++\n\t\t\tcontinue\n\t\t}\n\t\tusers = append(users, userEntry)\n\t}\n\n\t// read closing bracket\n\t_, err = dec.Token()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif numErrors > 0 {\n\t\terrMsg := fmt.Sprintf(\"Found %v errors.\", numErrors)\n\t\treturn nil, errors.New(errMsg)\n\t}\n\n\treturn users, nil\n}\n\n// commits the list of ImportEntries to the database. Wrapped in a transaction so if something fails no new users are added.\nfunc addToDatabase(entries []ImportOrExportEntry, persister persistence.Persister) error {\n\ttx := persister.GetConnection()\n\terr := tx.Transaction(func(tx *pop.Connection) error {\n\t\timporter := Importer{\n\t\t\tpersister:       persister,\n\t\t\ttx:              tx,\n\t\t\timportTimestamp: time.Now().UTC(),\n\t\t}\n\t\tfor i, v := range entries {\n\t\t\tuserModel, err := importer.createUser(v)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create user entry nr. %d: %w\", i, err)\n\t\t\t}\n\n\t\t\tfor _, e := range v.Emails {\n\t\t\t\temailModel, err := importer.createEmailAddress(userModel.ID, e)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create email address \\\"%s\\\" for user entry nr. %d: %w\", e.Address, i, err)\n\t\t\t\t}\n\t\t\t\tif e.IsPrimary {\n\t\t\t\t\terr = importer.createPrimaryEmailAddress(userModel.ID, emailModel.ID)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to set email \\\"%s\\\" as primary for user entry nr. %d: %w\", e.Address, i, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif v.Username != nil {\n\t\t\t\terr = importer.createUsername(userModel.ID, *v.Username)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create username \\\"%v\\\" for user entry nr. %d: %w\", v.Username, i, err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, credential := range v.WebauthnCredentials {\n\t\t\t\terr = importer.createWebauthnCredential(userModel.ID, credential)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create webauthn credential \\\"%s\\\" for user entry nr. %d: %w\", credential.ID, i, err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif v.Password != nil {\n\t\t\t\terr = importer.createPasswordCredential(userModel.ID, *v.Password)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create password for user entry nr. %d: %w\", i, err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif v.OTPSecret != nil {\n\t\t\t\terr = importer.createOTPSecret(userModel.ID, *v.OTPSecret)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create otp secret for user entry nr. %d: %w\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\n\treturn err\n}\n"
  },
  {
    "path": "backend/cmd/user/import_test.go",
    "content": "package user\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n)\n\nconst validUUID2 = \"799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3\"\n\nfunc TestImportSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(importSuite))\n}\n\ntype importSuite struct {\n\ttest.Suite\n}\n\nfunc (s *importSuite) Test_loadAndValidate() {\n\ttype args struct {\n\t\tinput io.Reader\n\t}\n\tstandardTime, _ := time.Parse(time.RFC3339, \"2023-06-07T13:42:49.369489Z\")\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    []ImportOrExportEntry\n\t\twantErr assert.ErrorAssertionFunc\n\t}{\n\t\t{\n\t\t\tname: \"empty array -> empty result\",\n\t\t\targs: args{\n\t\t\t\tinput: strings.NewReader(\"[]\"),\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t\twant:    []ImportOrExportEntry{},\n\t\t},\n\t\t{\n\t\t\tname: \"empty file -> nil result\",\n\t\t\targs: args{\n\t\t\t\tinput: strings.NewReader(\"\"),\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t\twant:    nil,\n\t\t},\n\t\t{\n\t\t\tname: \"one user file\",\n\t\t\targs: args{\n\t\t\t\tinput: strings.NewReader(\"[{\\\"user_id\\\":\\\"799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3\\\",\\\"emails\\\":[{\\\"address\\\":\\\"koreyrath@wolff.name\\\",\\\"is_primary\\\":true,\\\"is_verified\\\":true}],\\\"created_at\\\":\\\"2023-06-07T13:42:49.369489Z\\\",\\\"updated_at\\\":\\\"2023-06-07T13:42:49.369489Z\\\"}]\\n\"),\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t\twant: []ImportOrExportEntry{\n\t\t\t\t{\n\t\t\t\t\tUserID: validUUID2,\n\t\t\t\t\tEmails: Emails{\n\t\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\t\tAddress:    \"koreyrath@wolff.name\",\n\t\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\t\tIsVerified: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tCreatedAt: &standardTime,\n\t\t\t\t\tUpdatedAt: &standardTime,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"corrupted json input\",\n\t\t\targs: args{\n\t\t\t\tinput: strings.NewReader(\"[{user_id:\\\"799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3\\\",}]\\n\"),\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t\twant:    nil,\n\t\t},\n\t\t{\n\t\t\tname: \"several validation errors\",\n\t\t\targs: args{\n\t\t\t\tinput: strings.NewReader(\"[{\\\"user_id\\\":\\\"799e95f0-4cc7-4bd7-9f1-5fdc4fa26ea3\\\",\\\"emails\\\":[{\\\"address\\\":\\\"koreyrath@wolff.name\\\",\\\"is_primary\\\":true,\\\"is_verified\\\":true}],\\\"created_at\\\":\\\"2023-06-07T13:42:49.369489Z\\\",\\\"updated_at\\\":\\\"2023-06-07T13:42:49.369489Z\\\"},{\\\"user_id\\\":\\\"799e95f0-4cc7-4bd7-9f1-5fdc4fa26ea3\\\",\\\"emails\\\":[{\\\"address\\\":\\\"koreyrath@wolff.name\\\",\\\"is_primary\\\":false,\\\"is_verified\\\":true}],\\\"created_at\\\":\\\"2023-06-07T13:42:49.369489Z\\\",\\\"updated_at\\\":\\\"2023-06-07T13:42:49.369489Z\\\"}]\\n\"),\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t\twant:    nil,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\ts.Run(tt.name, func() {\n\t\t\tgot, err := loadAndValidate(tt.args.input)\n\t\t\tif !tt.wantErr(s.T(), err, fmt.Sprintf(\"loadAndValidate(%v)\", tt.args.input)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.Equalf(s.T(), tt.want, got, \"loadAndValidate(%v)\", tt.args.input)\n\t\t})\n\t}\n}\n\nfunc (s *importSuite) Test_addToDatabase() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttype args struct {\n\t\tentries   []ImportOrExportEntry\n\t\tpersister persistence.Persister\n\t}\n\ttests := []struct {\n\t\tname         string\n\t\targs         args\n\t\twantErr      assert.ErrorAssertionFunc\n\t\twantNumUsers int\n\t}{\n\t\t{\n\t\t\tname: \"Positive\",\n\t\t\targs: args{\n\t\t\t\tentries: []ImportOrExportEntry{\n\t\t\t\t\t{\n\t\t\t\t\t\tUserID: \"\",\n\t\t\t\t\t\tEmails: Emails{\n\t\t\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCreatedAt: nil,\n\t\t\t\t\t\tUpdatedAt: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpersister: s.Storage,\n\t\t\t},\n\t\t\twantErr:      assert.NoError,\n\t\t\twantNumUsers: 1,\n\t\t},\n\t\t{\n\t\t\tname: \"Double uuid\",\n\t\t\targs: args{\n\t\t\t\tentries: []ImportOrExportEntry{\n\t\t\t\t\t{\n\t\t\t\t\t\tUserID: validUUID,\n\t\t\t\t\t\tEmails: Emails{\n\t\t\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\t\t\tAddress:    \"primary1@hanko.io\",\n\t\t\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCreatedAt: nil,\n\t\t\t\t\t\tUpdatedAt: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tUserID: validUUID,\n\t\t\t\t\t\tEmails: Emails{\n\t\t\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\t\t\tAddress:    \"primary2@hanko.io\",\n\t\t\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCreatedAt: nil,\n\t\t\t\t\t\tUpdatedAt: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpersister: s.Storage,\n\t\t\t},\n\t\t\twantErr:      assert.Error,\n\t\t\twantNumUsers: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Double primary email\",\n\t\t\targs: args{\n\t\t\t\tentries: []ImportOrExportEntry{\n\t\t\t\t\t{\n\t\t\t\t\t\tUserID: validUUID,\n\t\t\t\t\t\tEmails: Emails{\n\t\t\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCreatedAt: nil,\n\t\t\t\t\t\tUpdatedAt: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tUserID: validUUID,\n\t\t\t\t\t\tEmails: Emails{\n\t\t\t\t\t\t\tImportOrExportEmail{\n\t\t\t\t\t\t\t\tAddress:    \"primary@hanko.io\",\n\t\t\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t\t\t\tIsVerified: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCreatedAt: nil,\n\t\t\t\t\t\tUpdatedAt: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpersister: s.Storage,\n\t\t\t},\n\t\t\twantErr:      assert.Error,\n\t\t\twantNumUsers: 0,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\ts.T().Run(tt.name, func(t *testing.T) {\n\n\t\t\ts.SetupTest()\n\t\t\ttt.wantErr(t, addToDatabase(tt.args.entries, tt.args.persister), fmt.Sprintf(\"addToDatabase(%v, %v)\", tt.args.entries, tt.args.persister))\n\t\t\tusers, err := tt.args.persister.GetUserPersister().List(0, 100, []uuid.UUID{}, \"\", \"\", \"\")\n\t\t\tlog.Println(users)\n\t\t\ts.NoError(err)\n\t\t\ts.Equal(tt.wantNumUsers, len(users))\n\n\t\t\ts.TearDownTest()\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/cmd/user/importer.go",
    "content": "package user\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype Importer struct {\n\tpersister       persistence.Persister\n\ttx              *pop.Connection\n\timportTimestamp time.Time\n}\n\nfunc (i *Importer) createUser(newUser ImportOrExportEntry) (*models.User, error) {\n\tuserID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tuserModel := models.User{\n\t\tID:        userID,\n\t\tCreatedAt: i.importTimestamp,\n\t\tUpdatedAt: i.importTimestamp,\n\t}\n\n\tif newUser.UserID != \"\" {\n\t\tuserModel.ID = uuid.FromStringOrNil(newUser.UserID)\n\t}\n\n\tif newUser.CreatedAt != nil {\n\t\tuserModel.CreatedAt = newUser.CreatedAt.UTC()\n\t}\n\n\tif newUser.UpdatedAt != nil {\n\t\tuserModel.UpdatedAt = newUser.UpdatedAt.UTC()\n\t}\n\n\terr = i.persister.GetUserPersisterWithConnection(i.tx).Create(userModel)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &userModel, nil\n}\n\nfunc (i *Importer) createEmailAddress(userID uuid.UUID, newEmail ImportOrExportEmail) (*models.Email, error) {\n\temailID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\temailModel := models.Email{\n\t\tID:        emailID,\n\t\tUserID:    &userID,\n\t\tAddress:   strings.ToLower(newEmail.Address),\n\t\tVerified:  newEmail.IsVerified,\n\t\tCreatedAt: i.importTimestamp,\n\t\tUpdatedAt: i.importTimestamp,\n\t}\n\n\terr = i.persister.GetEmailPersisterWithConnection(i.tx).Create(emailModel)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &emailModel, nil\n}\n\nfunc (i *Importer) createPrimaryEmailAddress(userID uuid.UUID, emailID uuid.UUID) error {\n\tentryID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tprimaryEmailModel := models.PrimaryEmail{\n\t\tID:        entryID,\n\t\tEmailID:   emailID,\n\t\tUserID:    userID,\n\t\tCreatedAt: i.importTimestamp,\n\t\tUpdatedAt: i.importTimestamp,\n\t}\n\n\terr = i.persister.GetPrimaryEmailPersisterWithConnection(i.tx).Create(primaryEmailModel)\n\treturn err\n}\n\nfunc (i *Importer) createUsername(userID uuid.UUID, username string) error {\n\tentryID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tusernameModel := models.Username{\n\t\tID:        entryID,\n\t\tUserId:    userID,\n\t\tUsername:  username,\n\t\tCreatedAt: i.importTimestamp,\n\t\tUpdatedAt: i.importTimestamp,\n\t}\n\n\terr = i.persister.GetUsernamePersisterWithConnection(i.tx).Create(usernameModel)\n\treturn err\n}\n\nfunc (i *Importer) createWebauthnCredential(userID uuid.UUID, webauthnCredential ImportWebauthnCredential) error {\n\tcreatedAt := i.importTimestamp\n\tupdatedAt := i.importTimestamp\n\tif webauthnCredential.CreatedAt != nil {\n\t\tcreatedAt = webauthnCredential.CreatedAt.UTC()\n\t}\n\n\tif webauthnCredential.UpdatedAt != nil {\n\t\tupdatedAt = webauthnCredential.UpdatedAt.UTC()\n\t}\n\n\tvar transports models.Transports = nil\n\tfor _, transport := range webauthnCredential.Transports {\n\t\ttransportID, err := uuid.NewV4()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttransports = append(transports, models.WebauthnCredentialTransport{\n\t\t\tID:                   transportID,\n\t\t\tName:                 transport,\n\t\t\tWebauthnCredentialID: webauthnCredential.ID,\n\t\t})\n\t}\n\n\twebauthnCredentialModel := models.WebauthnCredential{\n\t\tID:              webauthnCredential.ID,\n\t\tName:            webauthnCredential.Name,\n\t\tUserId:          userID,\n\t\tPublicKey:       webauthnCredential.PublicKey,\n\t\tAttestationType: webauthnCredential.AttestationType,\n\t\tAAGUID:          webauthnCredential.AAGUID,\n\t\tSignCount:       webauthnCredential.SignCount,\n\t\tLastUsedAt:      webauthnCredential.LastUsedAt,\n\t\tCreatedAt:       createdAt,\n\t\tUpdatedAt:       updatedAt,\n\t\tTransports:      transports,\n\t\tBackupEligible:  webauthnCredential.BackupEligible,\n\t\tBackupState:     webauthnCredential.BackupState,\n\t\tMFAOnly:         webauthnCredential.MFAOnly,\n\t}\n\n\tif webauthnCredential.UserHandle != nil {\n\t\texistingUserHandle, err := i.persister.GetWebauthnCredentialUserHandlePersisterWithConnection(i.tx).GetByHandle(*webauthnCredential.UserHandle)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif existingUserHandle != nil {\n\t\t\twebauthnCredentialModel.UserHandleID = &existingUserHandle.ID\n\t\t} else {\n\t\t\tuserHandleID, err := uuid.NewV4()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tuserHandle := models.WebauthnCredentialUserHandle{\n\t\t\t\tID:        userHandleID,\n\t\t\t\tUserID:    userID,\n\t\t\t\tHandle:    *webauthnCredential.UserHandle,\n\t\t\t\tCreatedAt: i.importTimestamp,\n\t\t\t\tUpdatedAt: i.importTimestamp,\n\t\t\t}\n\t\t\twebauthnCredentialModel.UserHandle = &userHandle\n\t\t\twebauthnCredentialModel.UserHandleID = &userHandleID\n\t\t}\n\t}\n\n\terr := i.persister.GetWebauthnCredentialPersisterWithConnection(i.tx).Create(webauthnCredentialModel)\n\treturn err\n}\n\nfunc (i *Importer) createPasswordCredential(userID uuid.UUID, passwordCredential ImportPasswordCredential) error {\n\tpasswordID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcreatedAt := i.importTimestamp\n\tupdatedAt := i.importTimestamp\n\tif passwordCredential.CreatedAt != nil {\n\t\tcreatedAt = passwordCredential.CreatedAt.UTC()\n\t}\n\n\tif passwordCredential.UpdatedAt != nil {\n\t\tupdatedAt = passwordCredential.UpdatedAt.UTC()\n\t}\n\n\tpasswordModel := models.PasswordCredential{\n\t\tID:        passwordID,\n\t\tUserId:    userID,\n\t\tPassword:  passwordCredential.Password,\n\t\tCreatedAt: createdAt,\n\t\tUpdatedAt: updatedAt,\n\t}\n\n\terr = i.persister.GetPasswordCredentialPersisterWithConnection(i.tx).Create(passwordModel)\n\treturn err\n}\n\nfunc (i *Importer) createOTPSecret(userID uuid.UUID, otpSecret ImportOTPSecret) error {\n\totpSecretID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcreatedAt := i.importTimestamp\n\tupdatedAt := i.importTimestamp\n\tif otpSecret.CreatedAt != nil {\n\t\tcreatedAt = otpSecret.CreatedAt.UTC()\n\t}\n\n\tif otpSecret.UpdatedAt != nil {\n\t\tupdatedAt = otpSecret.UpdatedAt.UTC()\n\t}\n\n\totpSecretModel := models.OTPSecret{\n\t\tID:        otpSecretID,\n\t\tUserID:    userID,\n\t\tSecret:    otpSecret.Secret,\n\t\tCreatedAt: createdAt,\n\t\tUpdatedAt: updatedAt,\n\t}\n\n\terr = i.persister.GetOTPSecretPersisterWithConnection(i.tx).Create(otpSecretModel)\n\treturn err\n}\n"
  },
  {
    "path": "backend/cmd/user/root.go",
    "content": "package user\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewUserCommand() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"user\",\n\t\tShort: \"User import/export tools\",\n\t\tLong:  `Add the ability to import/export users into/from the hanko database.`,\n\t}\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcommand := NewUserCommand()\n\tparent.AddCommand(command)\n\tcommand.AddCommand(NewImportCommand())\n\tcommand.AddCommand(NewGenerateCommand())\n\tcommand.AddCommand(NewExportCommand())\n}\n"
  },
  {
    "path": "backend/cmd/version/version.go",
    "content": "package version\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/build_info\"\n)\n\nfunc NewVersionCommand() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"version\",\n\t\tShort: \"Print the version and exit\",\n\t\tLong: `Prints the version of the hanko binary.\nFor all non 'clean' semver tags (e.g. vX.Y.Z) the format is the following: vX.Y.Z-CC-CH[-dirty].\nvX.Y.Z: the last tagged semver tag\nCC: Commits since the last tag\nCH: The commit short hash of the current commit\n[-dirty]: is appended if there are any changes that are not committed yet`,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tfmt.Println(build_info.GetVersion())\n\t\t},\n\t}\n}\n\nfunc RegisterCommands(parent *cobra.Command) {\n\tcmd := NewVersionCommand()\n\tparent.AddCommand(cmd)\n}\n"
  },
  {
    "path": "backend/config/config.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/kelseyhightower/envconfig\"\n\t\"github.com/knadh/koanf/parsers/yaml\"\n\t\"github.com/knadh/koanf/providers/file\"\n\t\"github.com/knadh/koanf/v2\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n)\n\n// Config is the central configuration type\ntype Config struct {\n\t// `account` configures settings related to user accounts.\n\tAccount Account `yaml:\"account\" json:\"account,omitempty\" koanf:\"account\" jsonschema:\"title=account\"`\n\t// `audit_log` configures output and storage modalities of audit logs.\n\tAuditLog AuditLog `yaml:\"audit_log\" json:\"audit_log,omitempty\" koanf:\"audit_log\" split_words:\"true\" jsonschema:\"title=audit_log\"`\n\t// `convert_legacy_config`, if set to `true`, automatically copies the set values of deprecated configuration\n\t// options, to new ones. If set to `false`, these values have to be set manually if non-default values should be\n\t// used.\n\tConvertLegacyConfig bool `yaml:\"convert_legacy_config\" json:\"convert_legacy_config,omitempty\" koanf:\"convert_legacy_config\" split_words:\"true\" jsonschema:\"default=false\"`\n\t// `covert_legacy_session_config`, if set to `true`, automatically copies the set of deprecated server-side session\n\t// configuration options to the new ones. If set to `false`, these values have to be set manually if non-default\n\t// values should be used.\n\tConvertLegacyServerSideSessionConfig bool `yaml:\"convert_legacy_server_side_session_config\" json:\"convert_legacy_server_side_session_config,omitempty\" koanf:\"convert_legacy_server_side_session_config\" split_words:\"true\" jsonschema:\"default=true\"`\n\t// `database` configures database connection settings.\n\tDatabase Database `yaml:\"database\" json:\"database,omitempty\" koanf:\"database\" jsonschema:\"title=database\"`\n\t// `debug`, if set to `true`, adds additional debugging information to flow API responses.\n\tDebug bool `yaml:\"debug\" json:\"debug,omitempty\" koanf:\"debug\" jsonschema:\"default=false\"`\n\t// `email` configures how email addresses of user accounts are acquired and used.\n\tEmail Email `yaml:\"email\" json:\"email,omitempty\" koanf:\"email\" jsonschema:\"title=email\"`\n\t// `email_delivery` configures how outgoing mails are delivered.\n\tEmailDelivery EmailDelivery `yaml:\"email_delivery\" json:\"email_delivery,omitempty\" koanf:\"email_delivery\" split_words:\"true\" jsonschema:\"title=email_delivery\"`\n\t// Deprecated. See child properties for suggested replacements.\n\tEmails Emails `yaml:\"emails\" json:\"emails,omitempty\" koanf:\"emails\" jsonschema:\"title=emails\"`\n\t// `flow_locker` confgures flow locking\n\tFlowLocker FlowLocker `yaml:\"flow_locker\" json:\"flow_locker,omitempty\" koanf:\"flow_locker\"`\n\t// `log` configures application logging.\n\tLog LoggerConfig `yaml:\"log\" json:\"log,omitempty\" koanf:\"log\" jsonschema:\"title=log\"`\n\t// `mfa` configures how multi-factor-authentication behaves.\n\tMFA MFA `yaml:\"mfa\" json:\"mfa,omitempty\" koanf:\"mfa\" jsonschema:\"title=mfa\"`\n\t// Deprecated. See child properties for suggested replacements.\n\tPasscode Passcode `yaml:\"passcode\" json:\"passcode,omitempty\" koanf:\"passcode\" jsonschema:\"title=passcode\"`\n\t// `passkey` configures how passkeys  are acquired and used.\n\tPasskey Passkey `yaml:\"passkey\" json:\"passkey,omitempty\" koanf:\"passkey\" jsonschema:\"title=passkey\"`\n\t// `password` configures how passwords are acquired and used.\n\tPassword Password `yaml:\"password\" json:\"password,omitempty\" koanf:\"password\" jsonschema:\"title=password\"`\n\t// `rate_limiter` configures rate limits for rate limited API operations and storage modalities for rate limit data.\n\tRateLimiter RateLimiter `yaml:\"rate_limiter\" json:\"rate_limiter,omitempty\" koanf:\"rate_limiter\" split_words:\"true\" jsonschema:\"title=rate_limiter\"`\n\t// `saml` configures modalities of SAML (Security Assertion Markup Language) SSO authentication and SAML identity\n\t// providers.\n\tSaml config.Saml `yaml:\"saml\" json:\"saml,omitempty\" koanf:\"saml\" jsonschema:\"title=saml\"`\n\t// `secrets` configures the keys used for cryptographically signing tokens issued by the API.\n\tSecrets Secrets `yaml:\"secrets\" json:\"secrets,omitempty\" koanf:\"secrets\" jsonschema:\"title=secrets\"`\n\t// `security_notifications` configures security notifications for important security-related events.\n\tSecurityNotifications SecurityNotifications `yaml:\"security_notifications\" json:\"security_notifications,omitempty\" koanf:\"security_notifications\"`\n\t// `server` configures address and CORS settings of the public and admin API.\n\tServer Server `yaml:\"server\" json:\"server,omitempty\" koanf:\"server\" jsonschema:\"title=server\"`\n\t// `service` configures general service information.\n\tService Service `yaml:\"service\" json:\"service,omitempty\" koanf:\"service\" jsonschema:\"title=service\"`\n\t// `session` configures settings for session JWTs and Cookies issued by the API.\n\tSession Session `yaml:\"session\" json:\"session,omitempty\" koanf:\"session\" jsonschema:\"title=session\"`\n\t// Deprecated. Use `email_delivery.smtp` instead.\n\tSmtp SMTP `yaml:\"smtp\" json:\"smtp,omitempty\" koanf:\"smtp\" jsonschema:\"title=smtp\"`\n\t// `third_party` configures the modalities of third party OAuth/OIDC based authentication and available identity\n\t// providers.\n\tThirdParty ThirdParty `yaml:\"third_party\" json:\"third_party,omitempty\" koanf:\"third_party\" split_words:\"true\" jsonschema:\"title=third_party\"`\n\t// `username` configures how usernames of user accounts are acquired and used.\n\tUsername Username `yaml:\"username\" json:\"username,omitempty\" koanf:\"username\" jsonschema:\"title=username\"`\n\t// `webauthn` configures general settings for communication with the WebAuthentication API.\n\tWebauthn WebauthnSettings `yaml:\"webauthn\" json:\"webauthn,omitempty\" koanf:\"webauthn\" jsonschema:\"title=webauthn\"`\n\t// `webhooks` configures HTTP-based callbacks for specific events occurring in the system.\n\tWebhooks WebhookSettings `yaml:\"webhooks\" json:\"webhooks,omitempty\" koanf:\"webhooks\" jsonschema:\"title=webhooks\"`\n\t// `privacy` configures privacy settings\n\tPrivacy Privacy `yaml:\"privacy\" json:\"privacy,omitempty\" koanf:\"privacy\" jsonschema:\"title=privacy\"`\n}\n\nvar (\n\tDefaultConfigFilePath = \"./config/config.yaml\"\n)\n\nfunc LoadFile(filePath *string, pa koanf.Parser) (*koanf.Koanf, error) {\n\tk := koanf.New(\".\")\n\n\tif filePath == nil || *filePath == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tif err := k.Load(file.Provider(*filePath), pa); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load file from '%s': %w\", *filePath, err)\n\t}\n\n\treturn k, nil\n}\n\nfunc Load(cfgFile *string) (*Config, error) {\n\tif cfgFile == nil || *cfgFile == \"\" {\n\t\t*cfgFile = DefaultConfigFilePath\n\t}\n\n\tc := DefaultConfig()\n\tk, err := LoadFile(cfgFile, yaml.Parser())\n\tif err != nil {\n\t\tif *cfgFile != DefaultConfigFilePath {\n\t\t\treturn nil, fmt.Errorf(\"failed to load config from: %s: %w\", *cfgFile, err)\n\t\t}\n\t\tlog.Println(\"failed to load config, skipping...\")\n\t} else {\n\t\tlog.Println(\"Using config file:\", *cfgFile)\n\t\terr = k.Unmarshal(\"\", c)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to unmarshal config: %w\", err)\n\t\t}\n\t}\n\n\terr = envconfig.Process(\"\", c)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load config from env vars: %w\", err)\n\t}\n\n\terr = c.PostProcess()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to post process config: %w\", err)\n\t}\n\n\tif err = c.Validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to validate config: %s\", err)\n\t}\n\n\treturn c, nil\n}\n\nfunc (c *Config) Validate() error {\n\terr := c.Server.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate server settings: %w\", err)\n\t}\n\terr = c.Webauthn.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate webauthn settings: %w\", err)\n\t}\n\tif c.EmailDelivery.Enabled {\n\t\terr = c.Smtp.Validate()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to validate smtp settings: %w\", err)\n\t\t}\n\t}\n\terr = c.Database.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate database settings: %w\", err)\n\t}\n\terr = c.Secrets.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate secrets: %w\", err)\n\t}\n\terr = c.Service.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate service settings: %w\", err)\n\t}\n\terr = c.Session.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate session settings: %w\", err)\n\t}\n\terr = c.RateLimiter.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate rate-limiter settings: %w\", err)\n\t}\n\terr = c.ThirdParty.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate third_party settings: %w\", err)\n\t}\n\terr = c.Saml.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate saml settings: %w\", err)\n\t}\n\terr = c.Webhooks.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate webhook settings: %w\", err)\n\t}\n\terr = c.FlowLocker.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate flow_locker settings: %w\", err)\n\t}\n\terr = c.Email.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate email settings: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (c *Config) convertLegacyConfig() {\n\tc.Email.Limit = c.Emails.MaxNumOfAddresses\n\tc.Email.RequireVerification = c.Emails.RequireVerification\n\tc.Email.PasscodeTtl = c.Passcode.TTL\n\n\tc.EmailDelivery.SMTP = c.Smtp\n\n\tc.Password.MinLength = c.Password.MinPasswordLength\n\n\tc.Passkey.UserVerification = c.Webauthn.UserVerification\n\n\tc.Webauthn.Timeouts.Login = c.Webauthn.Timeout\n\tc.Webauthn.Timeouts.Registration = c.Webauthn.Timeout\n}\n\nfunc (c *Config) convertLegacyServerSideSessionConfig() {\n\tif c.Session.ServerSide != nil && c.Session.ServerSide.Enabled {\n\t\tc.Session.AllowRevocation = true\n\t\tc.Session.AcquireIPAddress = true\n\t\tc.Session.AcquireUserAgent = true\n\t\tc.Session.Limit = c.Session.ServerSide.Limit\n\t\tc.Session.ShowOnProfile = true\n\t}\n}\n\nfunc (c *Config) PostProcess() error {\n\tif c.ConvertLegacyConfig {\n\t\tc.convertLegacyConfig()\n\t}\n\n\tif c.ConvertLegacyServerSideSessionConfig {\n\t\tc.convertLegacyServerSideSessionConfig()\n\t}\n\n\terr := c.ThirdParty.PostProcess()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to post process third party settings: %w\", err)\n\t}\n\n\terr = c.Webauthn.PostProcess()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to post process webauthn settings: %w\", err)\n\t}\n\n\terr = c.Saml.PostProcess()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to post process saml settings: %w\", err)\n\t}\n\n\terr = c.Email.PostProcess()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to post process email settings: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config.yaml",
    "content": "audit_log:\n  storage:\n    enabled: false\n  retention: 720h\naccount:\n  allow_deletion: true\n  allow_signup: true\nconvert_legacy_config: false\ndatabase:\n  user: hanko\n  password: hanko\n  host: localhost\n  port: \"5432\"\n  dialect: postgres\ndebug: false\nemail:\n  enabled: true\n  optional: false\n  acquire_on_registration: true\n  acquire_on_login: false\n  require_verification: true\n  limit: 5\n  use_as_login_identifier: true\n  max_length: 100\n  use_for_authentication: true\n  passcode_ttl: 300\nemail_delivery:\n  enabled: true\n  from_address: noreply@hanko.io\n  from_name: Hanko\n  smtp:\n    host: localhost\n    port: \"2500\"\nlog:\n  log_health_and_metrics: true\nmfa:\n  acquire_on_login: false\n  acquire_on_registration: true\n  device_trust_cookie_name: hanko-device-token\n  device_trust_duration: 720h\n  device_trust_policy: prompt\n  enabled: true\n  optional: true\n  security_keys:\n    attestation_preference: direct\n    authenticator_attachment: cross-platform\n    enabled: true\n    limit: 10\n    user_verification: discouraged\n  totp:\n    enabled: true\npasskey:\n  enabled: true\n  optional: true\n  acquire_on_registration: always\n  acquire_on_login: always\n  attestation_preference: direct\n  user_verification: preferred\n  limit: 10\npassword:\n  enabled: true\n  optional: false\n  acquire_on_registration: always\n  acquire_on_login: never\n  recovery: true\n  min_length: 8\nrate_limiter:\n  enabled: true\n  store: in_memory\n  otp_limits:\n    tokens: 3\n    interval: 1m\n  passcode_limits:\n    tokens: 3\n    interval: 1m\n  password_limits:\n    tokens: 3\n    interval: 1m\n  token_limits:\n    tokens: 3\n    interval: 1m\nsaml:\n  enabled: false\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nserver:\n  public:\n    cors:\n      allow_origins:\n        - http://localhost:63342\n        - http://localhost:8888\n        - http://localhost:8000\nservice:\n  name: Hanko Authentication Service\nsession:\n  allow_revocation: true\n  acquire_ip_address: true\n  acquire_user_agent: true\n  lifespan: 12h\n  enable_auth_token_header: false\n  limit: 5\n  cookie:\n    http_only: true\n    retention: persistent\n    same_site: strict\n    secure: true\n  show_on_profile: true\nthird_party:\n  providers:\n    apple:\n      enabled: false\n    discord:\n      enabled: false\n    github:\n      enabled: false\n    google:\n      enabled: false\n    linkedin:\n      enabled: false\n    microsoft:\n      enabled: false\n    facebook:\n      enabled: false\nusername:\n  enabled: false\n  optional: true\n  acquire_on_registration: true\n  acquire_on_login: true\n  use_as_login_identifier: true\n  min_length: 3\n  max_length: 32\nwebauthn:\n  timeouts:\n    registration: 600000\n    login: 600000\n  relying_party:\n    id: localhost\n    origins:\n      - http://localhost:63342\n      - http://localhost:8888\n      - http://localhost:8000\nwebhooks:\n  enabled: false\n  allow_time_expiration: false\nsecurity_notifications:\n  notifications:\n    email_create:\n      enabled: true\n    email_delete:\n      enabled: true\n    password_update:\n      enabled: true\n    passkey_create:\n      enabled: true\n    primary_email_update:\n      enabled: true\n    mfa_create:\n      enabled: true\n    mfa_delete:\n      enabled: true"
  },
  {
    "path": "backend/config/config_account.go",
    "content": "package config\n\ntype Account struct {\n\t// `allow_deletion` determines whether users can delete their accounts.\n\tAllowDeletion bool `yaml:\"allow_deletion\" json:\"allow_deletion,omitempty\" koanf:\"allow_deletion\" jsonschema:\"default=false\"`\n\t// `allow_signup` determines whether users are able to create new accounts.\n\tAllowSignup bool `yaml:\"allow_signup\" json:\"allow_signup,omitempty\" koanf:\"allow_signup\" jsonschema:\"default=true\"`\n}\n"
  },
  {
    "path": "backend/config/config_audit_log.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\ntype AuditLog struct {\n\t// `console_output` controls audit log console output.\n\tConsoleOutput AuditLogConsole `yaml:\"console_output\" json:\"console_output,omitempty\" koanf:\"console_output\" split_words:\"true\" jsonschema:\"title=console_output\"`\n\t// `mask` determines whether sensitive information (usernames, emails) should be masked in the audit log output.\n\t//\n\t// This configuration applies to logs written to the console as well as persisted logs.\n\tMask bool `yaml:\"mask\" json:\"mask,omitempty\" koanf:\"mask\" jsonschema:\"default=true\"`\n\t// `storage` controls audit log retention.\n\tStorage AuditLogStorage `yaml:\"storage\" json:\"storage,omitempty\" koanf:\"storage\"`\n\t// `retention` specifies the time duration after which log audit entries may be deleted.\n\tRetention string `yaml:\"retention\" json:\"retention,omitempty\" koanf:\"retention\" jsonschema:\"default=720h\"`\n}\n\nfunc (al *AuditLog) Validate() error {\n\t_, err := time.ParseDuration(al.Retention)\n\tif err != nil {\n\t\treturn errors.New(\"failed to parse retention_duration\")\n\t}\n\treturn nil\n}\n\ntype AuditLogStorage struct {\n\t// `enabled` controls whether audit log should be retained (i.e. persisted).\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n}\n\ntype AuditLogConsole struct {\n\t// `enabled` controls whether audit log output on the console is enabled or disabled.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// `output` determines the output stream audit logs are sent to.\n\tOutputStream OutputStream `yaml:\"output\" json:\"output,omitempty\" koanf:\"output\" split_words:\"true\" jsonschema:\"default=stdout,enum=stdout,enum=stderr\"`\n}\n\ntype OutputStream string\n\nvar (\n\tOutputStreamStdOut OutputStream = \"stdout\"\n\tOutputStreamStdErr OutputStream = \"stderr\"\n)\n"
  },
  {
    "path": "backend/config/config_database.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype Database struct {\n\t// `database` determines the name of the database schema to use.\n\tDatabase string `yaml:\"database\" json:\"database,omitempty\" koanf:\"database\" jsonschema:\"default=hanko\"`\n\t// `dialect` is the name of the database system to use.\n\tDialect string `yaml:\"dialect\" json:\"dialect,omitempty\" koanf:\"dialect\" jsonschema:\"default=postgres,enum=postgres,enum=mysql,enum=mariadb,enum=cockroach\"`\n\t// `host` is the host the database system is running on.\n\tHost string `yaml:\"host\" json:\"host,omitempty\" koanf:\"host\" jsonschema:\"default=localhost\"`\n\t// `password` is the password for the database user to use for connecting to the database.\n\tPassword string `yaml:\"password\" json:\"password,omitempty\" koanf:\"password\" jsonschema:\"default=hanko\"`\n\t// `port` is the port the database system is running on.\n\tPort string `yaml:\"port\" json:\"port,omitempty\" koanf:\"port\" jsonschema:\"default=5432\"`\n\t// `url` is a datasource connection string. It can be used instead of the rest of the database configuration\n\t// options. If this `url` is set then it is prioritized, i.e. the rest of the options, if set, have no effect.\n\t//\n\t// Schema: `dialect://username:password@host:port/database`\n\tUrl string `yaml:\"url\" json:\"url,omitempty\" koanf:\"url\" jsonschema:\"example=postgres://hanko:hanko@localhost:5432/hanko\"`\n\t// `user` is the database user to use for connecting to the database.\n\tUser string `yaml:\"user\" json:\"user,omitempty\" koanf:\"user\" jsonschema:\"default=hanko\"`\n}\n\nfunc (d *Database) Validate() error {\n\tif len(strings.TrimSpace(d.Url)) > 0 {\n\t\treturn nil\n\t}\n\tif len(strings.TrimSpace(d.Database)) == 0 {\n\t\treturn errors.New(\"database must not be empty\")\n\t}\n\tif len(strings.TrimSpace(d.User)) == 0 {\n\t\treturn errors.New(\"user must not be empty\")\n\t}\n\tif len(strings.TrimSpace(d.Host)) == 0 {\n\t\treturn errors.New(\"host must not be empty\")\n\t}\n\tif len(strings.TrimSpace(d.Port)) == 0 {\n\t\treturn errors.New(\"port must not be empty\")\n\t}\n\tif len(strings.TrimSpace(d.Dialect)) == 0 {\n\t\treturn errors.New(\"dialect must not be empty\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_default.go",
    "content": "package config\n\nimport \"time\"\n\nfunc DefaultConfig() *Config {\n\treturn &Config{\n\t\tConvertLegacyConfig:                  false,\n\t\tConvertLegacyServerSideSessionConfig: true,\n\t\tService: Service{\n\t\t\tName: \"Hanko Authentication Service\",\n\t\t},\n\t\tSecrets: Secrets{\n\t\t\tKeys: []string{\"abcedfghijklmnopqrstuvwxyz\"},\n\t\t\tKeyManagement: KeyManagement{\n\t\t\t\tType: \"local\",\n\t\t\t},\n\t\t},\n\t\tServer: Server{\n\t\t\tPublic: ServerSettings{\n\t\t\t\tAddress: \":8000\",\n\t\t\t\tCors: Cors{\n\t\t\t\t\tAllowOrigins:                []string{\"http://localhost:8888\"},\n\t\t\t\t\tUnsafeWildcardOriginAllowed: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tAdmin: ServerSettings{\n\t\t\t\tAddress: \":8001\",\n\t\t\t},\n\t\t},\n\t\tWebauthn: WebauthnSettings{\n\t\t\tRelyingParty: RelyingParty{\n\t\t\t\tId:          \"localhost\",\n\t\t\t\tDisplayName: \"Hanko Authentication Service\",\n\t\t\t\tOrigins:     []string{\"http://localhost:8888\"},\n\t\t\t},\n\t\t\tUserVerification: \"preferred\",\n\t\t\tTimeout:          600000,\n\t\t\tTimeouts: WebauthnTimeouts{\n\t\t\t\tRegistration: 600000,\n\t\t\t\tLogin:        600000,\n\t\t\t},\n\t\t},\n\t\tSecurityNotifications: SecurityNotifications{\n\t\t\tNotifications: SecurityNotificationTypes{\n\t\t\t\tEmailCreate: SecurityNotificationConfiguration{\n\t\t\t\t\tEnabled: true,\n\t\t\t\t},\n\t\t\t\tEmailDelete: SecurityNotificationConfiguration{\n\t\t\t\t\tEnabled: true,\n\t\t\t\t},\n\t\t\t\tPasswordUpdate: SecurityNotificationConfiguration{\n\t\t\t\t\tEnabled: true,\n\t\t\t\t},\n\t\t\t\tPasskeyCreate: SecurityNotificationConfiguration{\n\t\t\t\t\tEnabled: true,\n\t\t\t\t},\n\t\t\t\tPrimaryEmailUpdate: SecurityNotificationConfiguration{\n\t\t\t\t\tEnabled: true,\n\t\t\t\t},\n\t\t\t\tMFACreate: SecurityNotificationConfiguration{\n\t\t\t\t\tEnabled: true,\n\t\t\t\t},\n\t\t\t\tMFADelete: SecurityNotificationConfiguration{\n\t\t\t\t\tEnabled: true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tSmtp: SMTP{\n\t\t\tHost: \"localhost\",\n\t\t\tPort: \"465\",\n\t\t},\n\t\tEmailDelivery: EmailDelivery{\n\t\t\tEnabled: true,\n\t\t\tSMTP: SMTP{\n\t\t\t\tHost: \"localhost\",\n\t\t\t\tPort: \"465\",\n\t\t\t},\n\t\t\tFromAddress: \"noreply@hanko.io\",\n\t\t\tFromName:    \"Hanko\",\n\t\t},\n\t\tPasscode: Passcode{\n\t\t\tTTL: 300,\n\t\t},\n\t\tPassword: Password{\n\t\t\tEnabled:               true,\n\t\t\tOptional:              false,\n\t\t\tAcquireOnRegistration: \"always\",\n\t\t\tAcquireOnLogin:        \"never\",\n\t\t\tRecovery:              true,\n\t\t\tMinLength:             8,\n\t\t},\n\t\tDatabase: Database{\n\t\t\tDatabase: \"hanko\",\n\t\t\tUser:     \"hanko\",\n\t\t\tPassword: \"hanko\",\n\t\t\tPort:     \"5432\",\n\t\t\tDialect:  \"postgres\",\n\t\t\tHost:     \"localhost\",\n\t\t},\n\t\tSession: Session{\n\t\t\tAllowRevocation:  true,\n\t\t\tAcquireIPAddress: true,\n\t\t\tAcquireUserAgent: true,\n\t\t\tLifespan:         \"12h\",\n\t\t\tCookie: Cookie{\n\t\t\t\tHttpOnly:  true,\n\t\t\t\tRetention: \"persistent\",\n\t\t\t\tSameSite:  \"strict\",\n\t\t\t\tSecure:    true,\n\t\t\t},\n\t\t\tLimit:         5,\n\t\t\tShowOnProfile: true,\n\t\t},\n\t\tAuditLog: AuditLog{\n\t\t\tConsoleOutput: AuditLogConsole{\n\t\t\t\tEnabled:      true,\n\t\t\t\tOutputStream: OutputStreamStdOut,\n\t\t\t},\n\t\t\tMask:      true,\n\t\t\tRetention: \"720h\",\n\t\t},\n\t\tEmails: Emails{\n\t\t\tRequireVerification: true,\n\t\t\tMaxNumOfAddresses:   5,\n\t\t},\n\t\tRateLimiter: RateLimiter{\n\t\t\tEnabled: true,\n\t\t\tStore:   RATE_LIMITER_STORE_IN_MEMORY,\n\t\t\tOTPLimits: RateLimits{\n\t\t\t\tTokens:   3,\n\t\t\t\tInterval: 1 * time.Minute,\n\t\t\t},\n\t\t\tPasswordLimits: RateLimits{\n\t\t\t\tTokens:   5,\n\t\t\t\tInterval: 1 * time.Minute,\n\t\t\t},\n\t\t\tPasscodeLimits: RateLimits{\n\t\t\t\tTokens:   3,\n\t\t\t\tInterval: 1 * time.Minute,\n\t\t\t},\n\t\t\tTokenLimits: RateLimits{\n\t\t\t\tTokens:   3,\n\t\t\t\tInterval: 1 * time.Minute,\n\t\t\t},\n\t\t},\n\t\tAccount: Account{\n\t\t\tAllowDeletion: false,\n\t\t\tAllowSignup:   true,\n\t\t},\n\t\tThirdParty: ThirdParty{\n\t\t\tProviders: ThirdPartyProviders{\n\t\t\t\tApple: ThirdPartyProvider{\n\t\t\t\t\tDisplayName:  \"Apple\",\n\t\t\t\t\tAllowLinking: true,\n\t\t\t\t\tID:           \"apple\",\n\t\t\t\t},\n\t\t\t\tDiscord: ThirdPartyProvider{\n\t\t\t\t\tDisplayName:  \"Discord\",\n\t\t\t\t\tAllowLinking: true,\n\t\t\t\t\tID:           \"discord\",\n\t\t\t\t},\n\t\t\t\tLinkedIn: ThirdPartyProvider{\n\t\t\t\t\tDisplayName:  \"LinkedIn\",\n\t\t\t\t\tAllowLinking: true,\n\t\t\t\t\tID:           \"linkedin\",\n\t\t\t\t},\n\t\t\t\tMicrosoft: ThirdPartyProvider{\n\t\t\t\t\tDisplayName:  \"Microsoft\",\n\t\t\t\t\tAllowLinking: true,\n\t\t\t\t\tID:           \"microsoft\",\n\t\t\t\t},\n\t\t\t\tGitHub: ThirdPartyProvider{\n\t\t\t\t\tDisplayName:  \"GitHub\",\n\t\t\t\t\tAllowLinking: true,\n\t\t\t\t\tID:           \"github\",\n\t\t\t\t},\n\t\t\t\tGoogle: ThirdPartyProvider{\n\t\t\t\t\tDisplayName:  \"Google\",\n\t\t\t\t\tAllowLinking: true,\n\t\t\t\t\tID:           \"google\",\n\t\t\t\t},\n\t\t\t\tFacebook: ThirdPartyProvider{\n\t\t\t\t\tDisplayName:  \"Facebook\",\n\t\t\t\t\tAllowLinking: true,\n\t\t\t\t\tID:           \"facebook\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tPasskey: Passkey{\n\t\t\tEnabled:               true,\n\t\t\tOptional:              true,\n\t\t\tAcquireOnRegistration: \"always\",\n\t\t\tAcquireOnLogin:        \"always\",\n\t\t\tUserVerification:      \"preferred\",\n\t\t\tAttestationPreference: \"direct\",\n\t\t\tLimit:                 10,\n\t\t},\n\t\tEmail: Email{\n\t\t\tEnabled:               true,\n\t\t\tOptional:              false,\n\t\t\tAcquireOnRegistration: true,\n\t\t\tAcquireOnLogin:        true,\n\t\t\tRequireVerification:   true,\n\t\t\tLimit:                 5,\n\t\t\tUseAsLoginIdentifier:  true,\n\t\t\tMaxLength:             120,\n\t\t\tUseForAuthentication:  true,\n\t\t\tPasscodeTtl:           300,\n\t\t\tPasscodeCharset:       PasscodeCharsetNumeric,\n\t\t},\n\t\tUsername: Username{\n\t\t\tEnabled:               false,\n\t\t\tOptional:              true,\n\t\t\tAcquireOnRegistration: true,\n\t\t\tAcquireOnLogin:        true,\n\t\t\tUseAsLoginIdentifier:  true,\n\t\t\tMinLength:             3,\n\t\t\tMaxLength:             32,\n\t\t},\n\t\tMFA: MFA{\n\t\t\tAcquireOnLogin:               false,\n\t\t\tAcquireOnRegistration:        true,\n\t\t\tDeviceTrustCookieName:        \"hanko-device-token\",\n\t\t\tDeviceTrustDuration:          30 * 24 * time.Hour, // 30 days\n\t\t\tDeviceTrustMaxUsersPerDevice: 20,\n\t\t\tDeviceTrustPolicy:            \"prompt\",\n\t\t\tEnabled:                      true,\n\t\t\tOptional:                     true,\n\t\t\tSecurityKeys: SecurityKeys{\n\t\t\t\tAttestationPreference:   \"direct\",\n\t\t\t\tAuthenticatorAttachment: \"cross-platform\",\n\t\t\t\tEnabled:                 true,\n\t\t\t\tLimit:                   10,\n\t\t\t\tUserVerification:        \"discouraged\",\n\t\t\t},\n\t\t\tTOTP: TOTP{\n\t\t\t\tEnabled: true,\n\t\t\t},\n\t\t},\n\t\tPrivacy: Privacy{\n\t\t\tShowAccountExistenceHints:  false,\n\t\t\tOnlyShowActualLoginMethods: false,\n\t\t},\n\t\tDebug: false,\n\t\tFlowLocker: FlowLocker{\n\t\t\tEnabled: true,\n\t\t\tStore:   FLOW_LOCKER_STORE_IN_MEMORY,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "backend/config/config_email.go",
    "content": "package config\n\nimport \"fmt\"\n\ntype Email struct {\n\t// `acquire_on_login` determines whether users, provided that they do not already have registered an email,\n\t//\tare prompted to provide an email on login.\n\tAcquireOnLogin bool `yaml:\"acquire_on_login\" json:\"acquire_on_login,omitempty\" koanf:\"acquire_on_login\" split_words:\"true\" jsonschema:\"default=false\"`\n\t// `acquire_on_registration` determines whether users are prompted to provide an email on registration.\n\tAcquireOnRegistration bool `yaml:\"acquire_on_registration\" json:\"acquire_on_registration,omitempty\" koanf:\"acquire_on_registration\" split_words:\"true\" jsonschema:\"default=true\"`\n\t// `enabled` determines whether emails are enabled.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// 'limit' determines the maximum number of emails a user can register.\n\tLimit int `yaml:\"limit\" json:\"limit,omitempty\" koanf:\"limit\" jsonschema:\"default=5\"`\n\t// `max_length` specifies the maximum allowed length of an email address.\n\tMaxLength int `yaml:\"max_length\" json:\"max_length,omitempty\" koanf:\"max_length\" jsonschema:\"default=100\"`\n\t// `optional` determines whether users must provide an email when prompted.\n\t// There must always be at least one email address associated with an account. The primary email address cannot be\n\t// deleted if emails are required (`optional`: false`).\n\tOptional bool `yaml:\"optional\" json:\"optional,omitempty\" koanf:\"optional\" jsonschema:\"default=false\"`\n\t// `passcode_ttl` specifies, in seconds, how long a passcode is valid for.\n\tPasscodeTtl int `yaml:\"passcode_ttl\" json:\"passcode_ttl,omitempty\" koanf:\"passcode_ttl\" jsonschema:\"default=300\"`\n\t// `passcode_charset` specifies the characters that can be used in passcodes.\n\t// E.g. `numeric` allows only numbers, `alphanumeric` allows both numbers and letters.\n\tPasscodeCharset PasscodeCharset `yaml:\"passcode_charset\" json:\"passcode_charset,omitempty\" koanf:\"passcode_charset\" jsonschema:\"default=numeric,enum=numeric,enum=alphanumeric\"`\n\t// `require_verification` determines whether newly created emails must be verified by providing a passcode sent\n\t// to respective address.\n\tRequireVerification bool `yaml:\"require_verification\" json:\"require_verification,omitempty\" koanf:\"require_verification\" split_words:\"true\" jsonschema:\"default=true\"`\n\t// `use_as_login_identifier` determines whether emails can be used as an identifier on login.\n\tUseAsLoginIdentifier bool `yaml:\"use_as_login_identifier\" json:\"use_as_login_identifier,omitempty\" koanf:\"use_as_login_identifier\" jsonschema:\"default=true\"`\n\t// `user_for_authentication` determines whether users can log in by providing an email address and subsequently\n\t// providing a passcode sent to the given email address.\n\tUseForAuthentication bool `yaml:\"use_for_authentication\" json:\"use_for_authentication,omitempty\" koanf:\"use_for_authentication\" jsonschema:\"default=true\"`\n}\n\ntype PasscodeCharset string\n\nvar (\n\tPasscodeCharsetNumeric      PasscodeCharset = \"numeric\"\n\tPasscodeCharsetAlphanumeric PasscodeCharset = \"alphanumeric\"\n)\n\nfunc (e *Email) Validate() error {\n\tswitch e.PasscodeCharset {\n\tcase PasscodeCharsetNumeric, PasscodeCharsetAlphanumeric:\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"invalid passcode_characters: %s (allowed: 'numeric', 'alphanumeric')\", e.PasscodeCharset)\n}\n\nfunc (e *Email) PostProcess() error {\n\tif e.PasscodeCharset == \"\" {\n\t\te.PasscodeCharset = PasscodeCharsetNumeric\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_email_delivery.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype EmailDelivery struct {\n\t// `enabled` determines whether the API delivers emails.\n\t// Disable if you want to send the emails yourself. To do so you must subscribe to the `email.create` webhook event.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// `from_address` configures the sender address of emails sent to users.\n\tFromAddress string `yaml:\"from_address\" json:\"from_address,omitempty\" koanf:\"from_address\" split_words:\"true\" jsonschema:\"default=noreply@hanko.io\"`\n\t// `from_name` configures the sender name of emails sent to users.\n\tFromName string `yaml:\"from_name\" json:\"from_name,omitempty\" koanf:\"from_name\" split_words:\"true\" jsonschema:\"default=Hanko\"`\n\t// `SMTP` contains the SMTP server settings for sending mails.\n\tSMTP SMTP `yaml:\"smtp\" json:\"smtp,omitempty\" koanf:\"smtp\" jsonschema:\"title=smtp\"`\n}\n\n// SMTP Server Settings for sending passcodes\ntype SMTP struct {\n\tHost     string `yaml:\"host\" json:\"host,omitempty\" koanf:\"host\" jsonschema:\"default=localhost\"`\n\tPort     string `yaml:\"port\" json:\"port,omitempty\" koanf:\"port\" jsonschema:\"default=465\"`\n\tUser     string `yaml:\"user\" json:\"user,omitempty\" koanf:\"user\"`\n\tPassword string `yaml:\"password\" json:\"password,omitempty\" koanf:\"password\"`\n}\n\nfunc (s *SMTP) Validate() error {\n\tif len(strings.TrimSpace(s.Host)) == 0 {\n\t\treturn errors.New(\"smtp host must not be empty\")\n\t}\n\tif len(strings.TrimSpace(s.Port)) == 0 {\n\t\treturn errors.New(\"smtp port must not be empty\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_emails.go",
    "content": "package config\n\ntype Emails struct {\n\t// Deprecated. Use `email.require_verification` instead.\n\tRequireVerification bool `yaml:\"require_verification\" json:\"require_verification,omitempty\" koanf:\"require_verification\" split_words:\"true\" jsonschema:\"default=true\"`\n\t// Deprecated. Use `email.limit` instead.\n\tMaxNumOfAddresses int `yaml:\"max_num_of_addresses\" json:\"max_num_of_addresses,omitempty\" koanf:\"max_num_of_addresses\" split_words:\"true\" jsonschema:\"default=5\"`\n}\n"
  },
  {
    "path": "backend/config/config_flow_locker.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\ntype FlowLocker struct {\n\t// `enabled` controls whether flow locking is enabled\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// `store` sets the backend for the flow locker\n\tStore FlowLockerStoreType `yaml:\"store\" json:\"store,omitempty\" koanf:\"store\" jsonschema:\"default=in_memory,enum=in_memory,enum=redis\"`\n\t// `redis_config` configures connection to a redis instance\n\t// Required if `store` is set to `redis`\n\tRedis *RedisConfig `yaml:\"redis_config\" json:\"redis_config,omitempty\" koanf:\"redis_config\"`\n\t// `ttl` is the lock timeout (for Redis only)\n\tTTL time.Duration `yaml:\"ttl\" json:\"ttl,omitempty\" koanf:\"ttl\" jsonschema:\"default=30s,type=string\"`\n}\n\ntype FlowLockerStoreType string\n\nconst (\n\tFLOW_LOCKER_STORE_IN_MEMORY FlowLockerStoreType = \"in_memory\"\n\tFLOW_LOCKER_STORE_REDIS     FlowLockerStoreType = \"redis\"\n)\n\nfunc (f *FlowLocker) Validate() error {\n\tif f.Enabled {\n\t\tswitch f.Store {\n\t\tcase FLOW_LOCKER_STORE_REDIS:\n\t\t\tif f.Redis == nil {\n\t\t\t\treturn errors.New(\"when enabling the redis store you have to specify the redis config\")\n\t\t\t}\n\t\t\tif f.Redis.Address == \"\" {\n\t\t\t\treturn errors.New(\"when enabling the redis store you have to specify the address where hanko can reach the redis instance\")\n\t\t\t}\n\t\tcase FLOW_LOCKER_STORE_IN_MEMORY:\n\t\t\tbreak\n\t\tdefault:\n\t\t\treturn errors.New(string(f.Store) + \" is not a valid flow locker store\")\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_logger.go",
    "content": "package config\n\ntype LoggerConfig struct {\n\t// `log_health_and_metrics` determines whether requests of the `/health` and `/metrics` endpoints are logged.\n\tLogHealthAndMetrics bool `yaml:\"log_health_and_metrics,omitempty\" json:\"log_health_and_metrics\" koanf:\"log_health_and_metrics\" jsonschema:\"default=true\"`\n}\n"
  },
  {
    "path": "backend/config/config_mfa.go",
    "content": "package config\n\nimport (\n\t\"github.com/invopop/jsonschema\"\n\t\"time\"\n)\n\ntype SecurityKeys struct {\n\t// `attestation_preference` is used to specify the preference regarding attestation conveyance during\n\t// credential generation.\n\tAttestationPreference string `yaml:\"attestation_preference\" json:\"attestation_preference,omitempty\" koanf:\"attestation_preference\" split_words:\"true\" jsonschema:\"default=direct,enum=direct,enum=indirect,enum=none\"`\n\t// `authenticator_attachment`  is used to specify the preference regarding authenticator attachment during credential registration.\n\tAuthenticatorAttachment string `yaml:\"authenticator_attachment\" json:\"authenticator_attachment,omitempty\" koanf:\"authenticator_attachment\" split_words:\"true\" jsonschema:\"default=cross-platform,enum=platform,enum=cross-platform,enum=no_preference\"`\n\t// `enabled` determines whether security keys are eligible for multi-factor-authentication.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// 'limit' determines the maximum number of security keys a user can register.\n\tLimit int `yaml:\"limit\" json:\"limit,omitempty\" koanf:\"limit\" jsonschema:\"default=10\"`\n\t// `user_verification` specifies the requirements regarding local authorization with an authenticator through\n\t//  various authorization gesture modalities; for example, through a touch plus pin code,\n\t//  password entry, or biometric recognition.\n\t//\n\t// The setting applies to both WebAuthn registration and authentication ceremonies.\n\tUserVerification string `yaml:\"user_verification\" json:\"user_verification,omitempty\" koanf:\"user_verification\" split_words:\"true\" jsonschema:\"default=discouraged,enum=required,enum=preferred,enum=discouraged\"`\n}\n\ntype TOTP struct {\n\t// `enabled` determines whether TOTP is eligible for multi-factor-authentication.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled\" koanf:\"enabled\" jsonschema:\"default=true\"`\n}\n\ntype MFA struct {\n\t// `acquire_on_login` configures if users are prompted creating an MFA credential on login.\n\tAcquireOnLogin bool `yaml:\"acquire_on_login\" json:\"acquire_on_login\" koanf:\"acquire_on_login\" jsonschema:\"default=false\"`\n\t// `acquire_on_registration` configures if users are prompted creating an MFA credential on registration.\n\tAcquireOnRegistration bool `yaml:\"acquire_on_registration\" json:\"acquire_on_registration\" koanf:\"acquire_on_registration\" jsonschema:\"default=true\"`\n\t// `device_trust_cookie_name` is the name of the cookie used to store the token of a trusted device.\n\tDeviceTrustCookieName string `yaml:\"device_trust_cookie_name\" json:\"device_trust_cookie_name,omitempty\" koanf:\"device_trust_cookie_name\" jsonschema:\"default=hanko_device_token\"`\n\t// `device_trust_duration` configures the duration a device remains trusted after authentication; once expired, the\n\t// user must reauthenticate with MFA.\n\tDeviceTrustDuration time.Duration `yaml:\"device_trust_duration\" json:\"device_trust_duration\" koanf:\"device_trust_duration\" jsonschema:\"default=720h,type=string\"`\n\t// `device_trust_max_users_per_device` limits how many users can have device trust on a single device/browser.\n\t// Oldest entries are removed when the limit is exceeded. This allows multiple users to trust the same device\n\t// without overwriting each other's trust tokens.\n\tDeviceTrustMaxUsersPerDevice int `yaml:\"device_trust_max_users_per_device\" json:\"device_trust_max_users_per_device,omitempty\" koanf:\"device_trust_max_users_per_device\" jsonschema:\"default=20\"`\n\t// `device_trust_policy` determines the conditions under which a device or browser is considered trusted, allowing\n\t// MFA to be skipped for subsequent logins.\n\tDeviceTrustPolicy string `yaml:\"device_trust_policy\" json:\"device_trust_policy,omitempty\" koanf:\"device_trust_policy\" split_words:\"true\" jsonschema:\"default=prompt,enum=always,enum=prompt,enum=never\"`\n\t// `enabled` determines whether multi-factor-authentication is enabled.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// `optional` determines whether users must create an MFA credential when prompted. The MFA credential cannot be\n\t// deleted if multi-factor-authentication is required (`optional: false`).\n\tOptional bool `yaml:\"optional\" json:\"optional\" koanf:\"optional\" jsonschema:\"default=true\"`\n\t// `security_keys` configures security key settings for multi-factor-authentication\n\tSecurityKeys SecurityKeys `yaml:\"security_keys\" json:\"security_keys,omitempty\" koanf:\"security_keys\" jsonschema:\"title=security_keys\"`\n\t// `totp` configures the TOTP (Time-Based One-Time-Password) method for multi-factor-authentication.\n\tTOTP TOTP `yaml:\"totp\" json:\"totp,omitempty\" koanf:\"totp\" jsonschema:\"title=totp\"`\n}\n\nfunc (MFA) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tdeviceTrustPolicy, _ := schema.Properties.Get(\"device_trust_policy\")\n\tdeviceTrustPolicy.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"always\": \"Devices are trusted without user consent until the trust expires, so MFA is skipped during subsequent logins.\",\n\t\t\"prompt\": \"The user can choose to trust the current device to skip MFA for subsequent logins.\",\n\t\t\"never\":  \"Devices are considered untrusted, so MFA is required for each login.\",\n\t}}\n}\n"
  },
  {
    "path": "backend/config/config_passcode.go",
    "content": "package config\n\ntype Passcode struct {\n\t// Deprecated. Use `email.passcode_ttl` instead.\n\tTTL int `yaml:\"ttl\" json:\"ttl,omitempty\" koanf:\"ttl\" jsonschema:\"default=300\"`\n}\n"
  },
  {
    "path": "backend/config/config_passkey.go",
    "content": "package config\n\nimport \"github.com/invopop/jsonschema\"\n\ntype Passkey struct {\n\t// `acquire_on_registration` configures how users are prompted creating a passkey on registration.\n\tAcquireOnRegistration string `yaml:\"acquire_on_registration\" json:\"acquire_on_registration,omitempty\" koanf:\"acquire_on_registration\" split_words:\"true\" jsonschema:\"default=always,enum=always,enum=conditional,enum=never\"`\n\t// `acquire_on_login` configures how users are prompted creating a passkey on login.\n\tAcquireOnLogin string `yaml:\"acquire_on_login\" json:\"acquire_on_login,omitempty\" koanf:\"acquire_on_login\" split_words:\"true\" jsonschema:\"default=always,enum=always,enum=conditional,enum=never\"`\n\t// `attestation_preference` is used to specify the preference regarding attestation conveyance during\n\t// credential generation.\n\tAttestationPreference string `yaml:\"attestation_preference\" json:\"attestation_preference,omitempty\" koanf:\"attestation_preference\" split_words:\"true\" jsonschema:\"default=direct,enum=direct,enum=indirect,enum=none\"`\n\t// `enabled` determines whether users can create or authenticate with passkeys.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// `limit` defines the maximum number of passkeys a user can have.\n\tLimit int `yaml:\"limit\" json:\"limit,omitempty\" koanf:\"limit\" jsonschema:\"default=10\"`\n\t// `optional` determines whether users must create a passkey when prompted. The last remaining passkey cannot be\n\t// deleted if passkeys are required (`optional: false`).\n\t//\n\t// It also takes part in determining the order of password and passkey acquisition\n\t// on login and registration (see also `acquire_on_login` and `acquire_on_registration`): if one credential type is\n\t// required (`optional: false`) then that one takes precedence, i.e. is acquired first.\n\tOptional bool `yaml:\"optional\" json:\"optional,omitempty\" koanf:\"optional\" jsonschema:\"default=true\"`\n\t// `user_verification` specifies the requirements regarding local authorization with an authenticator through\n\t//  various authorization gesture modalities; for example, through a touch plus pin code,\n\t//  password entry, or biometric recognition.\n\t//\n\t// The setting applies to both WebAuthn registration and authentication ceremonies.\n\tUserVerification string `yaml:\"user_verification\" json:\"user_verification,omitempty\" koanf:\"user_verification\" split_words:\"true\" jsonschema:\"default=preferred,enum=required,enum=preferred,enum=discouraged\"`\n}\n\nfunc (Passkey) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tacquireOnRegistration, _ := schema.Properties.Get(\"acquire_on_registration\")\n\tacquireOnRegistration.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"always\": \"Indicates that users are always prompted to create a passkey on registration.\",\n\t\t\"conditional\": `Indicates that users are prompted to create a passkey on registration as long as the user does\n\t\t\t\t\t\tnot have a password.\n\n\t\t\t\t\t\tIf passwords are also conditionally acquired on registration, then users are given a choice as\n\t\t\t\t\t\tto what type of credential to create.`,\n\t\t\"never\": \"Indicates that users are never prompted to create a passkey on registration.\",\n\t}}\n\n\tacquireOnLogin, _ := schema.Properties.Get(\"acquire_on_login\")\n\tacquireOnLogin.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"always\": `Indicates that users are always prompted to create a passkey on login\n\t\t\t\t\tprovided that they do not already have a passkey.`,\n\t\t\"conditional\": `Indicates that users are prompted to create a passkey on login provided that\n\t\t\t\t\t\tthey do not already have a passkey and do not have a password.\n\n\t\t\t\t\t\tIf passkeys are also conditionally acquired on login then users are given a choice as to what\n\t\t\t\t\t\ttype of credential to register.`,\n\t\t\"never\": \"Indicates that users are never prompted to create a passkey on login.\",\n\t}}\n\n\tuserVerification, _ := schema.Properties.Get(\"user_verification\")\n\tuserVerification.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"required\": \"Indicates that user verification is always required.\",\n\t\t\"preferred\": `Indicates that user verification is preferred but will not fail the operation if no\n\t\t\t\t\t\tuser verification was performed.`,\n\t\t\"discouraged\": \"Indicates that no user verification should be performed.\",\n\t}}\n\n\tattestationPreference, _ := schema.Properties.Get(\"attestation_preference\")\n\tattestationPreference.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"direct\": `Indicates that the Relying Party wants to receive the attestation statement as generated by\n\t\t\t\t\tthe authenticator.`,\n\t\t\"indirect\": `Indicates that the Relying Party prefers an attestation conveyance yielding verifiable\n\t\t\t\t\tattestation statements, but allows the client to decide how to obtain such attestation statements.`,\n\t\t\"none\": `Indicates that the Relying Party is not interested in authenticator attestation.`,\n\t}}\n\n}\n"
  },
  {
    "path": "backend/config/config_password.go",
    "content": "package config\n\nimport \"github.com/invopop/jsonschema\"\n\ntype Password struct {\n\t// `acquire_on_registration` configures how users are prompted creating a password on registration.\n\tAcquireOnRegistration string `yaml:\"acquire_on_registration\" json:\"acquire_on_registration,omitempty\" koanf:\"acquire_on_registration\" split_words:\"true\" jsonschema:\"default=always,enum=always,enum=conditional,enum=never\"`\n\t// `acquire_on_login` configures how users are prompted creating a password on login.\n\tAcquireOnLogin string `yaml:\"acquire_on_login\" json:\"acquire_on_login,omitempty\" koanf:\"acquire_on_login\" split_words:\"true\" jsonschema:\"default=never,enum=always,enum=conditional,enum=never\"`\n\t// `enabled` determines whether passwords are enabled or disabled.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// `min_length` determines the minimum password length.\n\tMinLength int `yaml:\"min_length\" json:\"min_length,omitempty\" koanf:\"min_length\" split_words:\"true\" jsonschema:\"default=8\"`\n\t// Deprecated. Use `min_length` instead.\n\tMinPasswordLength int `yaml:\"min_password_length\" json:\"min_password_length,omitempty\" koanf:\"min_password_length\" split_words:\"true\" jsonschema:\"default=8\"`\n\t// `optional` determines whether users must set a password when prompted. The password cannot be deleted if\n\t// passwords are required (`optional: false`).\n\t//\n\t// It also takes part in determining the order of password and passkey acquisition\n\t// on login and registration (see also `acquire_on_login` and `acquire_on_registration`): if one credential type is\n\t// required (`optional: false`) then that one takes precedence, i.e. is acquired first.\n\tOptional bool `yaml:\"optional\" json:\"optional,omitempty\" koanf:\"optional\" jsonschema:\"default=false\"`\n\t// `recovery` determines whether users can start a recovery process, e.g. in case of a forgotten password.\n\tRecovery bool `yaml:\"recovery\" json:\"recovery,omitempty\" koanf:\"recovery\" jsonschema:\"default=true\"`\n}\n\nfunc (Password) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tacquireOnRegistration, _ := schema.Properties.Get(\"acquire_on_registration\")\n\tacquireOnRegistration.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"always\": \"Indicates that users are always prompted to create a password on registration.\",\n\t\t\"conditional\": `Indicates that users are prompted to create a password on registration as long as the user does\n\t\t\t\t\t\tnot have a passkey.\n\n\t\t\t\t\t\tIf passkeys are also conditionally acquired on registration, then users are given a choice as\n\t\t\t\t\t\tto what type of credential to register.`,\n\t\t\"never\": \"Indicates that users are never prompted to create a password on registration.\",\n\t}}\n\n\tacquireOnLogin, _ := schema.Properties.Get(\"acquire_on_login\")\n\tacquireOnLogin.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"always\": `Indicates that users are always prompted to create a password on login\n\t\t\t\t\tprovided that they do not already have a password.`,\n\t\t\"conditional\": `Indicates that users are prompted to create a password on login provided that\n\t\t\t\t\t\tthey do not already have a password and do not have a passkey.\n\n\t\t\t\t\t\tIf passkeys are also conditionally acquired on login then users are given a choice as to what\n\t\t\t\t\t\ttype of credential to register.`,\n\t\t\"never\": \"Indicates that users are never prompted to create a password on login.\",\n\t}}\n}\n"
  },
  {
    "path": "backend/config/config_privacy.go",
    "content": "package config\n\ntype Privacy struct {\n\t// `show_account_existence_hints` determines whether the user should get a user-friendly response rather than a privacy protecting one. E.g. on sign-up, when enabled the user will get \"user already exists\" response.\n\t// It only has an effect when emails are enabled.\n\tShowAccountExistenceHints bool `yaml:\"show_account_existence_hints\" json:\"show_account_existence_hints,omitempty\" koanf:\"show_account_existence_hints\" split_words:\"true\" jsonschema:\"default=false\"`\n\t// `only_show_actual_login_methods` determines whether the user will only be prompted with his configured login methods.\n\t// It only has an effect when emails are enabled, can be used for authentication and passwords are enabled.\n\tOnlyShowActualLoginMethods bool `yaml:\"only_show_actual_login_methods\" json:\"only_show_actual_login_methods,omitempty\" koanf:\"only_show_actual_login_methods\" split_words:\"true\" jsonschema:\"default=false\"`\n}\n"
  },
  {
    "path": "backend/config/config_rate_limiter.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\ntype RateLimiter struct {\n\t// `enabled` controls whether rate limiting is enabled or disabled.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n\t// `store` sets the store for the rate limiter. When you have multiple instances of Hanko running, it is recommended to use\n\t//  the `redis` store because otherwise your instances each have their own states.\n\tStore RateLimiterStoreType `yaml:\"store\" json:\"store,omitempty\" koanf:\"store\" jsonschema:\"default=in_memory,enum=in_memory,enum=redis\"`\n\t// `redis_config` configures connection to a redis instance.\n\t// Required if `store` is set to `redis`\n\tRedis *RedisConfig `yaml:\"redis_config\" json:\"redis_config,omitempty\" koanf:\"redis_config\"`\n\t// `passcode_limits` controls rate limits for passcode operations.\n\tPasscodeLimits RateLimits `yaml:\"passcode_limits\" json:\"passcode_limits,omitempty\" koanf:\"passcode_limits\" split_words:\"true\"`\n\t// `otp_limits` controls rate limits for OTP login attempts.\n\tOTPLimits RateLimits `yaml:\"otp_limits\" json:\"otp_limits,omitempty\" koanf:\"otp_limits\" split_words:\"true\"`\n\t// `password_limits` controls rate limits for password login operations.\n\tPasswordLimits RateLimits `yaml:\"password_limits\" json:\"password_limits,omitempty\" koanf:\"password_limits\" split_words:\"true\"`\n\t// `token_limits` controls rate limits for token exchange operations.\n\tTokenLimits RateLimits `yaml:\"token_limits\" json:\"token_limits,omitempty\" koanf:\"token_limits\" split_words:\"true\" jsonschema:\"default=token=3;interval=1m\"`\n}\n\ntype RateLimits struct {\n\t// `tokens` determines how many operations/requests can occur in the given `interval`.\n\tTokens uint64 `yaml:\"tokens\" json:\"tokens\" koanf:\"tokens\" jsonschema:\"default=3\"`\n\t// `interval` determines when to reset the token interval.\n\t// It must be a (possibly signed) sequence of decimal\n\t// numbers, each with optional fraction and a unit suffix, such as \"300ms\", \"-1.5h\" or \"2h45m\".\n\t// Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".\n\tInterval time.Duration `yaml:\"interval\" json:\"interval\" koanf:\"interval\" jsonschema:\"default=1m,type=string\"`\n}\n\ntype RateLimiterStoreType string\n\nconst (\n\tRATE_LIMITER_STORE_IN_MEMORY RateLimiterStoreType = \"in_memory\"\n\tRATE_LIMITER_STORE_REDIS                          = \"redis\"\n)\n\nfunc (r *RateLimiter) Validate() error {\n\tif r.Enabled {\n\t\tswitch r.Store {\n\t\tcase RATE_LIMITER_STORE_REDIS:\n\t\t\tif r.Redis == nil {\n\t\t\t\treturn errors.New(\"when enabling the redis store you have to specify the redis config\")\n\t\t\t}\n\t\t\tif r.Redis.Address == \"\" {\n\t\t\t\treturn errors.New(\"when enabling the redis store you have to specify the address where hanko can reach the redis instance\")\n\t\t\t}\n\t\tcase RATE_LIMITER_STORE_IN_MEMORY:\n\t\t\tbreak\n\t\tdefault:\n\t\t\treturn errors.New(string(r.Store) + \" is not a valid rate limiter store.\")\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_secrets.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/invopop/jsonschema\"\n\torderedmap \"github.com/wk8/go-ordered-map/v2\"\n)\n\ntype Secrets struct {\n\t// KeyManagement configures the key management system used for signing JWTs.\n\t// Supports 'local' (default) which uses the keys defined in the 'keys' field to encrypt a newly generated private\n\t// RSA key in the database, or 'aws_kms' which uses AWS Key Management Service for key signatures.\n\tKeyManagement KeyManagement `yaml:\"key_management\" json:\"key_management,omitempty\" koanf:\"key_management\"`\n\t// `keys` are used to en- and decrypt the JWKs which get used to sign the JWTs issued by the API.\n\t// For every key a JWK is generated, encrypted with the key and persisted in the database.\n\t//\n\t// You can use this list for key rotation: add a new key to the beginning of the list and the corresponding\n\t// JWK will then be used for signing JWTs. All tokens signed with the previous JWK(s) will still\n\t// be valid until they expire. Removing a key from the list does not remove the corresponding\n\t// database record. If you remove a key, you also have to remove the database record, otherwise\n\t// application startup will fail.\n\tKeys []string `yaml:\"keys\" json:\"keys,omitempty\" koanf:\"keys\"`\n}\n\nfunc (Secrets) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tkeys, _ := schema.Properties.Get(\"keys\")\n\tvar keysItemsMinLength uint64 = 16\n\tkeys.Items = &jsonschema.Schema{\n\t\tType:      \"string\",\n\t\tTitle:     \"keys\",\n\t\tMinLength: &keysItemsMinLength,\n\t}\n\n\t// Require at least one key when key_management.type is \"local\"\n\tschema.If = &jsonschema.Schema{\n\t\tProperties: func() *orderedmap.OrderedMap[string, *jsonschema.Schema] {\n\t\t\tprops := orderedmap.New[string, *jsonschema.Schema]()\n\t\t\tprops.Set(\"key_management\", &jsonschema.Schema{\n\t\t\t\tProperties: func() *orderedmap.OrderedMap[string, *jsonschema.Schema] {\n\t\t\t\t\tkmProps := orderedmap.New[string, *jsonschema.Schema]()\n\t\t\t\t\tkmProps.Set(\"type\", &jsonschema.Schema{\n\t\t\t\t\t\tConst: \"local\",\n\t\t\t\t\t})\n\t\t\t\t\treturn kmProps\n\t\t\t\t}(),\n\t\t\t})\n\t\t\treturn props\n\t\t}(),\n\t}\n\tvar minItems uint64 = 1\n\tschema.Then = &jsonschema.Schema{\n\t\tProperties: func() *orderedmap.OrderedMap[string, *jsonschema.Schema] {\n\t\t\tprops := orderedmap.New[string, *jsonschema.Schema]()\n\t\t\tprops.Set(\"keys\", &jsonschema.Schema{\n\t\t\t\tMinItems: &minItems,\n\t\t\t})\n\t\t\treturn props\n\t\t}(),\n\t}\n\n}\n\nfunc (s *Secrets) Validate() error {\n\tif len(s.Keys) == 0 && s.KeyManagement.Type == \"local\" {\n\t\treturn errors.New(\"at least one key must be defined\")\n\t}\n\treturn s.KeyManagement.Validate()\n}\n\ntype KeyManagement struct {\n\t// Type specifies the key management system to use. Supported values are 'local' (default) for local key storage\n\t// or 'aws_kms' for AWS Key Management Service.\n\t// When using 'aws_kms,' the AWS credentials must be set using the standard AWS credential chain (in order of precedence).\n\t// 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)\n\t// 2. Shared credentials file (~/.aws/credentials)\n\t// 3. Shared config file (~/.aws/config)\n\t// 4. IAM role for Amazon EC2 (via instance metadata service)\n\t// 5. IAM role for Amazon ECS (via container credentials)\n\t// 6. IAM role for Amazon EKS (via service account token)\n\tType KeyManagementStoreType `yaml:\"type\" json:\"type,omitempty\" koanf:\"type\"`\n\t// KeyID is the AWS KMS key identifier (ARN or alias) used for signing operations.\n\t// Required when Type is 'aws_kms'.\n\tKeyID string `yaml:\"key_id\" json:\"key_id,omitempty\" koanf:\"key_id\"`\n\t// Region is the AWS region where the KMS key is located.\n\t// Required when Type is 'aws_kms'.\n\tRegion string `yaml:\"region\" json:\"region,omitempty\" koanf:\"region\"`\n}\n\nfunc (KeyManagement) JSONSchemaExtend(schema *jsonschema.Schema) {\n\ttypeProperty, _ := schema.Properties.Get(\"type\")\n\ttypeProperty.Enum = []interface{}{KEY_MANAGEMENT_STORE_LOCAL, KEY_MANAGEMENT_STORE_AWS_KMS}\n\n\tschema.If = &jsonschema.Schema{\n\t\tProperties: func() *orderedmap.OrderedMap[string, *jsonschema.Schema] {\n\t\t\tprops := orderedmap.New[string, *jsonschema.Schema]()\n\t\t\tprops.Set(\"type\", &jsonschema.Schema{\n\t\t\t\tConst: \"aws_kms\",\n\t\t\t})\n\t\t\treturn props\n\t\t}(),\n\t}\n\tschema.Then = &jsonschema.Schema{\n\t\tRequired: []string{\"key_id\", \"region\"},\n\t}\n}\n\nfunc (k *KeyManagement) Validate() error {\n\tif k.Type == KEY_MANAGEMENT_STORE_AWS_KMS {\n\t\tif k.KeyID == \"\" {\n\t\t\treturn errors.New(\"key_id is required when key_management.type is aws_kms\")\n\t\t}\n\t\tif k.Region == \"\" {\n\t\t\treturn errors.New(\"region is required when key_management.type is aws_kms\")\n\t\t}\n\t}\n\tif k.Type != KEY_MANAGEMENT_STORE_LOCAL && k.Type != KEY_MANAGEMENT_STORE_AWS_KMS {\n\t\treturn fmt.Errorf(\"unsupported key_management.type: %s\", k.Type)\n\t}\n\treturn nil\n}\n\ntype KeyManagementStoreType string\n\nvar (\n\tKEY_MANAGEMENT_STORE_AWS_KMS KeyManagementStoreType = \"aws_kms\"\n\tKEY_MANAGEMENT_STORE_LOCAL   KeyManagementStoreType = \"local\"\n)\n"
  },
  {
    "path": "backend/config/config_security_notifications.go",
    "content": "package config\n\ntype SecurityNotifications struct {\n\tNotifications SecurityNotificationTypes `yaml:\"notifications\" json:\"notifications,omitempty\" koanf:\"notifications\"`\n}\n\ntype SecurityNotificationTypes struct {\n\tPasswordUpdate     SecurityNotificationConfiguration `yaml:\"password_update\" json:\"password_update,omitempty\" koanf:\"password_update\"`\n\tPrimaryEmailUpdate SecurityNotificationConfiguration `yaml:\"primary_email_update\" json:\"primary_email_update,omitempty\" koanf:\"primary_email_update\"`\n\tEmailCreate        SecurityNotificationConfiguration `yaml:\"email_create\" json:\"email_create,omitempty\" koanf:\"email_create\"`\n\tEmailDelete        SecurityNotificationConfiguration `yaml:\"email_delete\" json:\"email_delete,omitempty\" koanf:\"email_delete\"`\n\tPasskeyCreate      SecurityNotificationConfiguration `yaml:\"passkey_create\" json:\"passkey_create,omitempty\" koanf:\"passkey_create\"`\n\tMFACreate          SecurityNotificationConfiguration `yaml:\"mfa_create\" json:\"mfa_create,omitempty\" koanf:\"mfa_create\"`\n\tMFADelete          SecurityNotificationConfiguration `yaml:\"mfa_delete\" json:\"mfa_delete,omitempty\" koanf:\"mfa_delete\"`\n}\n\ntype SecurityNotificationConfiguration struct {\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=true\"`\n}\n"
  },
  {
    "path": "backend/config/config_server.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Server struct {\n\t// `public` contains the server configuration for the public API.\n\tPublic ServerSettings `yaml:\"public\" json:\"public,omitempty\" koanf:\"public\" jsonschema:\"title=public\"`\n\t// `admin` contains the server configuration for the admin API.\n\tAdmin ServerSettings `yaml:\"admin\" json:\"admin,omitempty\" koanf:\"admin\" jsonschema:\"title=admin\"`\n}\n\nfunc (s *Server) Validate() error {\n\terr := s.Public.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error validating public server settings: %w\", err)\n\t}\n\terr = s.Admin.Validate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error validating admin server settings: %w\", err)\n\t}\n\treturn nil\n}\n\ntype ServerSettings struct {\n\t// `address` is the address of the server to listen on in the form of host:port.\n\t//\n\t// See [net.Dial](https://pkg.go.dev/net#Dial) for details of the address format.\n\tAddress string `yaml:\"address\" json:\"address,omitempty\" koanf:\"address\"`\n\t// `cors` contains configuration options regarding Cross-Origin-Resource-Sharing.\n\tCors Cors `yaml:\"cors\" json:\"cors,omitempty\" koanf:\"cors\" jsonschema:\"title=cors\"`\n}\n\ntype Cors struct {\n\t// `allow_origins` determines the value of the Access-Control-Allow-Origin\n\t// response header. This header defines a list of [origins](https://developer.mozilla.org/en-US/docs/Glossary/Origin)\n\t// that may access the resource.\n\t//\n\t// The wildcard characters `*` and `?` are supported and are converted to regex fragments `.*` and `.` accordingly.\n\tAllowOrigins []string `yaml:\"allow_origins\" json:\"allow_origins,omitempty\" koanf:\"allow_origins\" split_words:\"true\" jsonschema:\"title=allow_origins,default=http://localhost:8888\"`\n\n\t// `unsafe_wildcard_origin_allowed` allows a wildcard `*` origin to be used with AllowCredentials\n\t// flag. In that case we consider any origin allowed and send it back to the client in an `Access-Control-Allow-Origin` header.\n\t//\n\t// This is INSECURE and potentially leads to [cross-origin](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties)\n\t// attacks. See also https://github.com/labstack/echo/issues/2400 for discussion on the subject.\n\t//\n\t// Optional. Default value is `false`.\n\tUnsafeWildcardOriginAllowed bool `yaml:\"unsafe_wildcard_origin_allowed\" json:\"unsafe_wildcard_origin_allowed,omitempty\" koanf:\"unsafe_wildcard_origin_allowed\" split_words:\"true\" jsonschema:\"title=unsafe_wildcard_origin_allowed,default=false\"`\n}\n\nfunc (cors *Cors) Validate() error {\n\tfor _, origin := range cors.AllowOrigins {\n\t\tif origin == \"*\" && !cors.UnsafeWildcardOriginAllowed {\n\t\t\treturn fmt.Errorf(\"found wildcard '*' origin in server.public.cors.allow_origins, if this is intentional set server.public.cors.unsafe_wildcard_origin_allowed to true\")\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *ServerSettings) Validate() error {\n\tif len(strings.TrimSpace(s.Address)) == 0 {\n\t\treturn errors.New(\"field Address must not be empty\")\n\t}\n\tif err := s.Cors.Validate(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_service.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype Service struct {\n\t// `name` determines the name of the service.\n\t// This value is used, e.g. in the subject header of outgoing emails.\n\tName string `yaml:\"name\" json:\"name,omitempty\" koanf:\"name\"`\n}\n\nfunc (s *Service) Validate() error {\n\tif len(strings.TrimSpace(s.Name)) == 0 {\n\t\treturn errors.New(\"field name must not be empty\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_session.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/invopop/jsonschema\"\n)\n\ntype Session struct {\n\t// `allow_revocation` allows users to revoke their own sessions.\n\tAllowRevocation bool `yaml:\"allow_revocation\" json:\"allow_revocation,omitempty\" koanf:\"allow_revocation\" jsonschema:\"default=true\"`\n\t// `audience` is a list of strings that identifies the recipients that the JWT is intended for.\n\t// The audiences are placed in the `aud` claim of the JWT.\n\t// If not set, it defaults to the value of the`webauthn.relying_party.id` configuration parameter.\n\tAudience []string `yaml:\"audience\" json:\"audience,omitempty\" koanf:\"audience\"`\n\t// `acquire_ip_address` stores the user's IP address in the database.\n\tAcquireIPAddress bool `yaml:\"acquire_ip_address\" json:\"acquire_ip_address,omitempty\" koanf:\"acquire_ip_address\" jsonschema:\"default=true\"`\n\t// `acquire_user_agent` stores the user's user agent in the database.\n\tAcquireUserAgent bool `yaml:\"acquire_user_agent\" json:\"acquire_user_agent,omitempty\" koanf:\"acquire_user_agent\" jsonschema:\"default=true\"`\n\t// `cookie` contains configuration for the session cookie issued on successful registration or login.\n\tCookie Cookie `yaml:\"cookie\" json:\"cookie,omitempty\" koanf:\"cookie\"`\n\t// `enable_auth_token_header` determines whether a session token (JWT) is returned in an `X-Auth-Token`\n\t// header after a successful authentication. This option should be set to `true` if API and client applications\n\t// run on different domains.\n\tEnableAuthTokenHeader bool `yaml:\"enable_auth_token_header\" json:\"enable_auth_token_header,omitempty\" koanf:\"enable_auth_token_header\" split_words:\"true\" jsonschema:\"default=false\"`\n\t// `issuer` is a string that identifies the principal (human user, an organization, or a service)\n\t// that issued the JWT. Its value is set in the `iss` claim of a JWT.\n\tIssuer string `yaml:\"issuer\" json:\"issuer,omitempty\" koanf:\"issuer\"`\n\t// `lifespan` determines the maximum duration for which a session token (JWT) is valid. It must be a (possibly signed) sequence of decimal\n\t// numbers, each with optional fraction and a unit suffix, such as \"300ms\", \"-1.5h\" or \"2h45m\".\n\t// Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".\n\tLifespan string `yaml:\"lifespan\" json:\"lifespan,omitempty\" koanf:\"lifespan\" jsonschema:\"default=12h\"`\n\t// `limit` determines the maximum number of server-side sessions a user can have. When the limit is exceeded,\n\t// older sessions are invalidated.\n\tLimit int `yaml:\"limit\" json:\"limit,omitempty\" koanf:\"limit\" jsonschema:\"default=5\"`\n\t// `show_on_profile` indicates that the sessions should be listed on the profile.\n\tShowOnProfile bool `yaml:\"show_on_profile\" json:\"show_on_profile,omitempty\" koanf:\"show_on_profile\" jsonschema:\"default=true\"`\n\t// Deprecated. Use settings in parent object.\n\t//`server_side` contains configuration for server-side sessions.\n\tServerSide *ServerSide `yaml:\"server_side\" json:\"server_side,omitempty\" koanf:\"server_side\"`\n\t// `jwt_template` defines a template for adding custom `claims` to session JWTs.\n\t//\n\t// These claims are processed at JWT generation time and can include static values,\n\t// templated strings using Go's text/template syntax, or nested structures (maps and slices).\n\t//\n\t// The template has access to user data via the `.User` field, which includes:\n\t// - `.User.UserID`: The user's unique ID (string)\n\t// - `.User.Email`: Email details (optional, with `.Address`, `.IsPrimary`, `.IsVerified`)\n\t// - `.User.Username`: The user's username (string, optional)\n\t//\n\t// Claims that fail to process (e.g., due to invalid templates) are logged and skipped,\n\t// ensuring JWT generation continues without interruption.\n\t//\n\t//\n\t// Example usage in YAML configuration:\n\t// ```yaml\n\t// session:\n\t//   lifespan: 24h\n\t//   jwt_template:\n\t//     claims:\n\t//       role: \"user\"                               # Static value\n\t//       user_email: \"{{.User.Email.Address}}\"      # Templated string\n\t//       is_verified: \"{{.User.Email.IsVerified}}\"  # Boolean from user data\n\t//       metadata:                                  # Nested map\n\t//         source: \"hanko\"\n\t//         greeting: \"Hello {{.User.Username}}\"\n\t//       scopes:                                    # Slice with templated value\n\t//         - \"read\"\n\t//         - \"write\"\n\t//         - \"{{if .User.Email.IsVerified}}admin{{else}}basic{{end}}\"\n\t// ```\n\t//\n\t// In this example:\n\t// - `role` is a static string (\"user\").\n\t// - `user_email` dynamically inserts the user's email address.\n\t// - `is_verified` inserts a boolean indicating email verification status.\n\t// - `metadata` is a nested map with a static `source` and a templated `greeting`.\n\t// - `scopes` is a slice combining static values and a conditional template.\n\t//\n\t// Notes:\n\t// - Claims with the following keys will be ignored because they are currently added to the JWT\n\t//   by default:\n\t//     - sub\n\t//     - iat\n\t//     - exp\n\t//     - aud\n\t//     - iss\n\t//     - email\n\t//     - username\n\t//     - session_id\n\t// - Templates must be valid Go `text/template` syntax. Invalid templates are logged and ignored.\n\t// - Boolean strings (\"true\" or \"false\") from templates are automatically converted to actual booleans.\n\t// - Use conditionals (e.g., `{{if .User.Email}}`) to handle optional fields safely.\n\t//\n\t// For more details on template syntax, see: https://pkg.go.dev/text/template\n\tJWTTemplate *JWTTemplate `yaml:\"jwt_template\" json:\"jwt_template,omitempty\" koanf:\"jwt_template\"`\n}\n\nfunc (s *Session) Validate() error {\n\t_, err := time.ParseDuration(s.Lifespan)\n\tif err != nil {\n\t\treturn errors.New(\"failed to parse lifespan\")\n\t}\n\n\treturn nil\n}\n\ntype Cookie struct {\n\t// `domain` is the domain the cookie will be bound to. Works for subdomains, but not cross-domain.\n\t// See the `session.enable_auth_token_header` configuration instead if the API and the client application run on\n\t// different domains.\n\tDomain string `yaml:\"domain\" json:\"domain,omitempty\" koanf:\"domain\" jsonschema:\"default=hanko\"`\n\t// `http_only` determines whether cookies are HTTP only or accessible by Javascript.\n\tHttpOnly bool `yaml:\"http_only\" json:\"http_only,omitempty\" koanf:\"http_only\" split_words:\"true\" jsonschema:\"default=true\"`\n\t// `name` is the name of the cookie.\n\tName string `yaml:\"name\" json:\"name,omitempty\" koanf:\"name\" jsonschema:\"default=hanko\"`\n\t// `retention` determines the retention behavior of authentication cookies.\n\tRetention string `yaml:\"retention\" json:\"retention,omitempty\" koanf:\"retention\" split_words:\"true\" jsonschema:\"default=persistent,enum=session,enum=persistent,enum=prompt\"`\n\t// `same_site` controls whether a cookie is sent with cross-site requests.\n\t// See [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value) for\n\t// more details.\n\tSameSite string `yaml:\"same_site\" json:\"same_site,omitempty\" koanf:\"same_site\" split_words:\"true\" jsonschema:\"default=strict,enum=strict,enum=lax,enum=none\"`\n\t// `secure` indicates whether the cookie is sent to the server only when a request is made with the https: scheme\n\t// (except on localhost).\n\t//\n\t// NOTE: `secure` must be set to `false` when working on `localhost` and with the Safari browser because it does\n\t// not store secure cookies on `localhost`.\n\tSecure bool `yaml:\"secure\" json:\"secure,omitempty\" koanf:\"secure\" jsonschema:\"default=true\"`\n}\n\nfunc (Cookie) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tretention, _ := schema.Properties.Get(\"retention\")\n\tretention.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"session\":    \"Issues a temporary cookie that lasts for the duration of the browser session.\",\n\t\t\"persistent\": \"Issues a cookie that remains stored on the user's device until it reaches its expiration date.\",\n\t\t\"prompt\":     \"Allows the user to choose whether to stay signed in. If the user selects 'Stay signed in', a persistent cookie is issued; a session cookie otherwise.\",\n\t}}\n}\n\nfunc (c *Cookie) GetName() string {\n\tif c.Name != \"\" {\n\t\treturn c.Name\n\t}\n\n\treturn \"hanko\"\n}\n\ntype ServerSide struct {\n\t// `enabled` determines whether server-side sessions are enabled.\n\t//\n\t// NOTE: When enabled the session endpoint must be used in order to check if a session is still valid.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n\t// `limit` determines the maximum number of server-side sessions a user can have. When the limit is exceeded,\n\t// older sessions are invalidated.\n\tLimit int `yaml:\"limit\" json:\"limit,omitempty\" koanf:\"limit\" jsonschema:\"default=100\"`\n}\n\ntype JWTTemplate struct {\n\tClaims map[string]interface{} `yaml:\"claims\" json:\"claims,omitempty\" koanf:\"claims\"`\n}\n"
  },
  {
    "path": "backend/config/config_shared.go",
    "content": "package config\n\nimport \"github.com/invopop/jsonschema\"\n\ntype RedisConfig struct {\n\t// `address` is the address of the redis instance in the form of `host[:port][/database]`.\n\tAddress string `yaml:\"address\" json:\"address\" koanf:\"address\"`\n\t// `password` is the password for the redis instance.\n\tPassword string `yaml:\"password\" json:\"password,omitempty\" koanf:\"password\"`\n}\n\nfunc (t RedisConfig) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tpassword, _ := schema.Properties.Get(\"password\")\n\tschema.Properties.Set(\"password\", &jsonschema.Schema{\n\t\tDescription: password.Description,\n\t\tAnyOf: []*jsonschema.Schema{\n\t\t\t{Type: \"string\"},\n\t\t\t{Type: \"null\"},\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "backend/config/config_test.go",
    "content": "package config\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDefaultConfigAccountParameters(t *testing.T) {\n\tcfg := DefaultConfig()\n\tassert.Equal(t, cfg.Account.AllowDeletion, false)\n\tassert.Equal(t, cfg.Account.AllowSignup, true)\n}\n\nfunc TestDefaultConfigSmtpParameters(t *testing.T) {\n\tcfg := DefaultConfig()\n\tassert.Equal(t, cfg.Smtp.Port, \"465\")\n}\n\nfunc TestParseValidConfig(t *testing.T) {\n\tconfigPath := \"./config.yaml\"\n\tcfg, err := Load(&configPath)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestRootSmtpPasscodeSmtpConflict(t *testing.T) {\n\tconfigPath := \"./root-passcode-smtp-config.yaml\"\n\t_, err := Load(&configPath)\n\tassert.NoError(t, err)\n}\n\nfunc TestMinimalConfigValidates(t *testing.T) {\n\tconfigPath := \"./minimal-config.yaml\"\n\tcfg, err := Load(&configPath)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestRateLimiterConfig(t *testing.T) {\n\tconfigPath := \"./minimal-config.yaml\"\n\tcfg, err := Load(&configPath)\n\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tcfg.RateLimiter.Enabled = true\n\tcfg.RateLimiter.Store = \"in_memory\"\n\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tcfg.RateLimiter.Store = \"redis\"\n\tif err := cfg.Validate(); err == nil {\n\t\tt.Error(\"when specifying redis, the redis config should also be specified\")\n\t}\n\tcfg.RateLimiter.Redis = &RedisConfig{\n\t\tAddress:  \"127.0.0.1:9876\",\n\t\tPassword: \"password\",\n\t}\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tcfg.RateLimiter.Store = \"notvalid\"\n\tif err := cfg.Validate(); err == nil {\n\t\tt.Error(\"notvalid is not a valid backend\")\n\t}\n}\n\nfunc TestFlowLockerConfig(t *testing.T) {\n\tconfigPath := \"./minimal-config.yaml\"\n\tcfg, err := Load(&configPath)\n\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tcfg.FlowLocker.Enabled = true\n\tcfg.FlowLocker.Store = \"in_memory\"\n\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tcfg.FlowLocker.Store = \"redis\"\n\tif err := cfg.Validate(); err == nil {\n\t\tt.Error(\"when specifying redis, the redis config should also be specified\")\n\t}\n\tcfg.FlowLocker.Redis = &RedisConfig{\n\t\tAddress:  \"127.0.0.1:9876\",\n\t\tPassword: \"password\",\n\t}\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tcfg.FlowLocker.Store = \"notvalid\"\n\tif err := cfg.Validate(); err == nil {\n\t\tt.Error(\"notvalid is not a valid backend\")\n\t}\n}\n\nfunc TestEnvironmentVariables(t *testing.T) {\n\terr := os.Setenv(\"SMTP_HOST\", \"valueFromEnvVars\")\n\trequire.NoError(t, err)\n\n\terr = os.Setenv(\"WEBAUTHN_RELYING_PARTY_ORIGINS\", \"https://hanko.io,https://auth.hanko.io\")\n\trequire.NoError(t, err)\n\n\tconfigPath := \"./minimal-config.yaml\"\n\tcfg, err := Load(&configPath)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, \"valueFromEnvVars\", cfg.Smtp.Host)\n\tassert.True(t, reflect.DeepEqual([]string{\"https://hanko.io\", \"https://auth.hanko.io\"}, cfg.Webauthn.RelyingParty.Origins))\n}\n\nfunc TestParseSecurityNotificationsConfig(t *testing.T) {\n\tconfigPath := \"./security-notifications-disabled-config.yaml\"\n\tcfg, err := Load(&configPath)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n\tnotificationsConfig := cfg.SecurityNotifications\n\n\tassert.False(t, notificationsConfig.Notifications.EmailCreate.Enabled)\n\tassert.False(t, notificationsConfig.Notifications.EmailDelete.Enabled)\n\tassert.False(t, notificationsConfig.Notifications.PrimaryEmailUpdate.Enabled)\n\tassert.False(t, notificationsConfig.Notifications.PasswordUpdate.Enabled)\n\tassert.False(t, notificationsConfig.Notifications.PasskeyCreate.Enabled)\n\tassert.False(t, notificationsConfig.Notifications.MFACreate.Enabled)\n\tassert.False(t, notificationsConfig.Notifications.MFADelete.Enabled)\n}\n\nfunc TestParseDefaultSecurityNotificationsConfig(t *testing.T) {\n\tcfg := DefaultConfig()\n\tif err := cfg.Validate(); err != nil {\n\t\tt.Error(err)\n\t}\n\tnotificationsConfig := cfg.SecurityNotifications\n\n\tassert.True(t, notificationsConfig.Notifications.EmailCreate.Enabled)\n\tassert.True(t, notificationsConfig.Notifications.EmailDelete.Enabled)\n\tassert.True(t, notificationsConfig.Notifications.PrimaryEmailUpdate.Enabled)\n\tassert.True(t, notificationsConfig.Notifications.PasswordUpdate.Enabled)\n\tassert.True(t, notificationsConfig.Notifications.PasskeyCreate.Enabled)\n\tassert.True(t, notificationsConfig.Notifications.MFACreate.Enabled)\n\tassert.True(t, notificationsConfig.Notifications.MFADelete.Enabled)\n}\n"
  },
  {
    "path": "backend/config/config_third_party.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/fatih/structs\"\n\t\"github.com/gobwas/glob\"\n\t\"github.com/invopop/jsonschema\"\n\torderedmap \"github.com/wk8/go-ordered-map/v2\"\n)\n\ntype ThirdParty struct {\n\t// `providers` contains the configurations for the available OAuth/OIDC identity providers.\n\tProviders ThirdPartyProviders `yaml:\"providers\" json:\"providers,omitempty\" koanf:\"providers\" jsonschema:\"title=providers,uniqueItems=true\"`\n\t// `custom_providers contains the configurations for custom OAuth/OIDC identity providers.\n\tCustomProviders CustomThirdPartyProviders `yaml:\"custom_providers\" json:\"custom_providers,omitempty\" koanf:\"custom_providers\" jsonschema:\"title=custom_providers\"`\n\t// `redirect_url` is the URL the third party provider redirects to with an authorization code. Must consist of the base URL\n\t// of your running Hanko backend instance and the `callback` endpoint of the API,\n\t// i.e. `{YOUR_BACKEND_INSTANCE}/thirdparty/callback.`\n\t//\n\t// Required if any of the [`providers`](#providers) are `enabled`.\n\tRedirectURL string `yaml:\"redirect_url\" json:\"redirect_url,omitempty\" koanf:\"redirect_url\" split_words:\"true\" jsonschema:\"example=https://yourinstance.com/thirdparty/callback\"`\n\t// `error_redirect_url` is the URL the backend redirects to if an error occurs during third party sign-in.\n\t// Errors are provided as 'error' and 'error_description' query params in the redirect location URL.\n\t//\n\t// When using the Hanko web components it should be the URL of the page that embeds the web component such that\n\t// errors can be processed properly by the web component.\n\t//\n\t// You do not have to add this URL to the 'allowed_redirect_urls', it is automatically included when validating\n\t// redirect URLs.\n\t//\n\t// Required if any of the [`providers`](#providers) are `enabled`. Must not have trailing slash.\n\tErrorRedirectURL string `yaml:\"error_redirect_url\" json:\"error_redirect_url,omitempty\" koanf:\"error_redirect_url\" split_words:\"true\"`\n\t// `default_redirect_url` is the URL the backend redirects to after it successfully verified\n\t// the response from any third party provider.\n\t//\n\t// Must not have trailing slash.\n\tDefaultRedirectURL string `yaml:\"default_redirect_url\" json:\"default_redirect_url,omitempty\" koanf:\"default_redirect_url\" split_words:\"true\"`\n\t// `allowed_redirect_urls` is a list of URLs the backend is allowed to redirect to after third party sign-in was\n\t// successful.\n\t//\n\t// Supports wildcard matching through globbing. e.g. `https://*.example.com` will allow `https://foo.example.com`\n\t// and `https://bar.example.com` to be accepted.\n\t//\n\t// Globbing is also supported for paths, e.g. `https://foo.example.com/*` will match `https://foo.example.com/page1`\n\t// and `https://foo.example.com/page2`.\n\t//\n\t// A double asterisk (`**`) acts as a \"super\"-wildcard/match-all.\n\t//\n\t// See [here](https://pkg.go.dev/github.com/gobwas/glob#Compile) for more on globbing.\n\t//\n\t// Must not be empty if any of the [`providers`](#providers) are `enabled`. URLs in the list must not have a trailing slash.\n\tAllowedRedirectURLS   []string             `yaml:\"allowed_redirect_urls\" json:\"allowed_redirect_urls,omitempty\" koanf:\"allowed_redirect_urls\" split_words:\"true\" jsonschema:\"minItems=1\"`\n\tAllowedRedirectURLMap map[string]glob.Glob `jsonschema:\"-\" yaml:\"-\" json:\"-\" koanf:\"-\"`\n}\n\nfunc (t *ThirdParty) Validate() error {\n\thasEnabledProviders := t.Providers.HasEnabled()\n\thasEnabledCustomProviders := t.CustomProviders.HasEnabled()\n\n\tif hasEnabledProviders || hasEnabledCustomProviders {\n\t\tif t.RedirectURL == \"\" {\n\t\t\treturn errors.New(\"redirect_url must be set\")\n\t\t}\n\n\t\tif t.ErrorRedirectURL == \"\" {\n\t\t\treturn errors.New(\"error_redirect_url must be set\")\n\t\t}\n\n\t\tif len(t.AllowedRedirectURLS) <= 0 {\n\t\t\treturn errors.New(\"at least one allowed redirect url must be set\")\n\t\t}\n\n\t\turls := append(t.AllowedRedirectURLS, t.ErrorRedirectURL)\n\t\tif t.DefaultRedirectURL != \"\" {\n\t\t\turls = append(urls, t.DefaultRedirectURL)\n\t\t}\n\t\tfor _, u := range urls {\n\t\t\tif strings.HasSuffix(u, \"/\") {\n\t\t\t\treturn fmt.Errorf(\"redirect url %s must not have trailing slash\", u)\n\t\t\t}\n\t\t}\n\t}\n\n\tif hasEnabledProviders {\n\t\terr := t.Providers.Validate()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to validate third party providers: %w\", err)\n\t\t}\n\t}\n\n\tif hasEnabledCustomProviders {\n\t\terr := t.CustomProviders.Validate()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to validate custom third party providers: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (t ThirdParty) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tschema.If = &jsonschema.Schema{\n\t\tAllOf: []*jsonschema.Schema{\n\t\t\tt.JSONSchemaNoBuiltInProviderEnabled(),\n\t\t\tt.JSONSchemaNoCustomProviderEnabled(),\n\t\t},\n\t}\n\tschema.Then = &jsonschema.Schema{\n\t\tRequired: []string{},\n\t}\n\tschema.Else = &jsonschema.Schema{\n\t\tRequired: []string{\"redirect_url\", \"error_redirect_url\", \"allowed_redirect_urls\"},\n\t}\n}\n\nfunc (t ThirdParty) JSONSchemaNoBuiltInProviderEnabled() *jsonschema.Schema {\n\tenabledFalseOrNullProperties := orderedmap.New[string, *jsonschema.Schema]()\n\tenabledFalseOrNullProperties.Set(\"enabled\", &jsonschema.Schema{\n\t\tAnyOf: []*jsonschema.Schema{\n\t\t\t{Const: false},\n\t\t\t{Const: \"null\"},\n\t\t},\n\t})\n\tenabledFalseOrNullSchema := &jsonschema.Schema{\n\t\tType: \"object\",\n\t\tPatternProperties: map[string]*jsonschema.Schema{\n\t\t\t\"^.*\": {Ref: \"#/$defs/ThirdPartyProvider\", Properties: enabledFalseOrNullProperties},\n\t\t},\n\t}\n\n\tproperties := orderedmap.New[string, *jsonschema.Schema]()\n\tproperties.Set(\"providers\", enabledFalseOrNullSchema)\n\n\treturn &jsonschema.Schema{Properties: properties}\n}\n\nfunc (t ThirdParty) JSONSchemaNoCustomProviderEnabled() *jsonschema.Schema {\n\tenabledFalseOrNullProperties := orderedmap.New[string, *jsonschema.Schema]()\n\tenabledFalseOrNullProperties.Set(\"enabled\", &jsonschema.Schema{\n\t\tAnyOf: []*jsonschema.Schema{\n\t\t\t{Const: false},\n\t\t\t{Type: \"null\"},\n\t\t},\n\t})\n\tenabledFalseOrNullSchema := &jsonschema.Schema{\n\t\tAdditionalProperties: &jsonschema.Schema{\n\t\t\tRef: \"#/$defs/CustomThirdPartyProvider\", Properties: enabledFalseOrNullProperties,\n\t\t},\n\t}\n\n\tproperties := orderedmap.New[string, *jsonschema.Schema]()\n\tproperties.Set(\"custom_providers\", enabledFalseOrNullSchema)\n\n\treturn &jsonschema.Schema{Properties: properties}\n}\n\nfunc (t *ThirdParty) PostProcess() error {\n\tt.AllowedRedirectURLMap = make(map[string]glob.Glob)\n\turls := append(t.AllowedRedirectURLS, t.ErrorRedirectURL)\n\tfor _, redirectUrl := range urls {\n\t\tg, err := glob.Compile(redirectUrl, '.', '/')\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed compile allowed redirect url glob: %w\", err)\n\t\t}\n\t\tt.AllowedRedirectURLMap[redirectUrl] = g\n\t}\n\n\tif t.CustomProviders != nil {\n\t\tproviders := make(map[string]CustomThirdPartyProvider)\n\t\tfor key, provider := range t.CustomProviders {\n\t\t\t// add prefix per default to ensure built-in and custom providers can be distinguished\n\t\t\tkeyLower := strings.ToLower(key)\n\t\t\tprovider.ID = \"custom_\" + keyLower\n\t\t\tproviders[keyLower] = provider\n\t\t}\n\t\tt.CustomProviders = providers\n\t}\n\n\treturn nil\n}\n\ntype CustomThirdPartyProviders map[string]CustomThirdPartyProvider\n\nfunc (p *CustomThirdPartyProviders) GetEnabled() []CustomThirdPartyProvider {\n\tvar enabledProviders []CustomThirdPartyProvider\n\tfor _, provider := range *p {\n\t\tif provider.Enabled {\n\t\t\tenabledProviders = append(enabledProviders, provider)\n\t\t}\n\t}\n\n\treturn enabledProviders\n}\n\nfunc (p *CustomThirdPartyProviders) HasEnabled() bool {\n\tfor _, provider := range *p {\n\t\tif provider.Enabled {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (p *CustomThirdPartyProviders) Validate() error {\n\tfor _, v := range p.GetEnabled() {\n\t\terr := v.Validate()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"failed to validate third party provider %s: %w\",\n\t\t\t\tstrings.TrimPrefix(v.ID, \"custom_\"),\n\t\t\t\terr,\n\t\t\t)\n\t\t}\n\t}\n\treturn nil\n}\n\ntype CustomThirdPartyProvider struct {\n\t// `acr_values` is a list of strings that specifies the Authentication Context Class Reference values that the\n\t// Authorization Server is being requested to use for processing this Authentication Request.\n\tAcrValues []string `yaml:\"acr_values\" json:\"acr_values,omitempty\" koanf:\"acr_values\"`\n\t// `allow_linking` indicates whether existing accounts can be automatically linked with this provider.\n\t//\n\t// Linking is based on matching one of the email addresses of an existing user account with the (primary)\n\t// email address of the third party provider account.\n\tAllowLinking bool `yaml:\"allow_linking\" json:\"allow_linking,omitempty\" koanf:\"allow_linking\" jsonschema:\"default=false\"`\n\t// `attribute_mapping` defines a map that associates a set of known standard OIDC conformant end-user claims\n\t// (the key of a map entry) at the Hanko backend to claims retrieved from a third party provider (the value of the\n\t// map entry). This is primarily necessary if a non-OIDC provider is configured/used in which case it is probable\n\t// that user data returned from the userinfo endpoint does not already conform to OIDC standard claims.\n\t//\n\t// Example: You configure an OAuth Provider (i.e. non-OIDC) and the provider's configured userinfo endpoint returns\n\t// an end-user's user ID at the provider not under a `sub` key in its JSON response but rather under a `user_id`\n\t// key. You would then configure an attribute mapping as follows:\n\t//\n\t// ```yaml\n\t//attribute_mapping:\n\t//  sub: user_id\n\t// ```\n\t//\n\t// See https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims for a list of known standard claims\n\t// that provider claims can be mapped into. Any other claims received from a provider are not discarded but are\n\t// retained internally in a `custom_claims` claim.\n\t//\n\t// Mappings are one-to-one mappings, complex mappings (e.g. mapping concatenations of two claims) are not possible.\n\tAttributeMapping map[string]string `yaml:\"attribute_mapping\" json:\"attribute_mapping,omitempty\" koanf:\"attribute_mapping\"`\n\t// URL of the provider's authorization endpoint where the end-user is redirected to authenticate and grant consent for\n\t// an application to access their resources.\n\t//\n\t// Required if `use_discovery` is false or omitted.\n\tAuthorizationEndpoint string `yaml:\"authorization_endpoint\" json:\"authorization_endpoint,omitempty\" koanf:\"authorization_endpoint\"`\n\t// `ID` is a unique identifier for the provider, derived from the key in the `custom_providers` map, by\n\t// concatenating the prefix \"custom_\". This allows distinguishing between built-in and custom providers at runtime.\n\tID string `jsonschema:\"-\" yaml:\"-\" json:\"-\" koanf:\"-\"`\n\t// `issuer` is the provider's issuer identifier. It should be a URL that uses the \"https\"\n\t//\tscheme and has no query or fragment components.\n\t//\n\t// Required if `use_discovery` is true.\n\tIssuer string `yaml:\"issuer\" json:\"issuer,omitempty\" koanf:\"issuer\"`\n\t// `client_id` is the ID of the OAuth/OIDC client. Must be obtained from the provider.\n\t//\n\t// Required if the provider is `enabled`.\n\tClientID string `yaml:\"client_id\" json:\"client_id,omitempty\" koanf:\"client_id\" split_words:\"true\"`\n\t// `display_name` is the name of the provider that is intended to be shown to an end-user.\n\t//\n\t// Required if the provider is `enabled`.\n\tDisplayName string `yaml:\"display_name\" json:\"display_name,omitempty\" koanf:\"display_name\"`\n\t// `enabled` indicates if the provider is enabled or disabled.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n\t// `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent.\n\t// Possible values are:\n\t// - login\n\t// - none\n\t// - consent\n\t// - select_account\n\t// Please note that not all providers support all values. Check the corresponding docs of the provider for supported values.\n\tPrompt string `yaml:\"prompt\" json:\"prompt,omitempty\" koanf:\"prompt\"`\n\t// `scopes` is a list of scopes requested from the provider that specify the level of access an application has to\n\t// a user's resources on a server, defining what actions the app can perform on behalf of the user.\n\t//\n\t// Required if the provider is `enabled`.\n\tScopes []string `yaml:\"scopes\" json:\"scopes,omitempty\" koanf:\"scopes,omitempty\"`\n\t// `secret` is the client secret for the OAuth/OIDC client. Must be obtained from the provider.\n\t//\n\t// Required if the provider is `enabled`.\n\tSecret string `yaml:\"secret\" json:\"secret,omitempty\" koanf:\"secret\"`\n\t// URL of the provider's token endpoint URL where an application exchanges an authorization code for an access\n\t// token, which is used to authenticate API requests on behalf of the end-user.\n\t//\n\t// Required if `use_discovery` is false or omitted.\n\tTokenEndpoint string `yaml:\"token_endpoint\" json:\"token_endpoint,omitempty\" koanf:\"token_endpoint\"`\n\t// `use_discovery` determines if configuration information about an OpenID Connect (OIDC) provider, such as\n\t// endpoint URLs and supported features,should be automatically retrieved, from a well-known\n\t// URL (typically /.well-known/openid-configuration).\n\tUseDiscovery bool `yaml:\"use_discovery\" json:\"use_discovery,omitempty\" koanf:\"use_discovery\" jsonschema:\"default=true\"`\n\t// URL of the provider's endpoint that returns claims about an authenticated end-user.\n\t//\n\t// Required if `use_discovery` is false or omitted.\n\tUserinfoEndpoint string `yaml:\"userinfo_endpoint\" json:\"userinfo_endpoint,omitempty\" koanf:\"userinfo_endpoint\"`\n}\n\nfunc (p *CustomThirdPartyProvider) Validate() error {\n\tif p.Enabled {\n\t\tif p.DisplayName == \"\" {\n\t\t\treturn errors.New(\"missing display_name\")\n\t\t}\n\t\tif p.ClientID == \"\" {\n\t\t\treturn errors.New(\"missing client_id\")\n\t\t}\n\t\tif p.Secret == \"\" {\n\t\t\treturn errors.New(\"missing client secret\")\n\t\t}\n\t\tif len(p.Scopes) == 0 {\n\t\t\treturn errors.New(\"missing scopes\")\n\t\t}\n\t\tif p.UseDiscovery == true {\n\t\t\tif p.Issuer == \"\" {\n\t\t\t\treturn errors.New(\"issuer must be set when use_discovery is set to true\")\n\t\t\t}\n\t\t} else {\n\t\t\tauthorizationEndpointSet := p.AuthorizationEndpoint != \"\"\n\t\t\ttokenEndpointSet := p.TokenEndpoint != \"\"\n\t\t\tuserinfoEndpointSet := p.UserinfoEndpoint != \"\"\n\n\t\t\tif !authorizationEndpointSet || !tokenEndpointSet || !userinfoEndpointSet {\n\t\t\t\treturn errors.New(\"authorization_endpoint, token_endpoint and userinfo_endpoint must be set when use_discovery is set to false or unset\")\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (CustomThirdPartyProvider) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tschema.Title = \"custom_provider\"\n\n\tenabledFalseOrNull := &jsonschema.Schema{Properties: orderedmap.New[string, *jsonschema.Schema]()}\n\tenabledFalseOrNull.Properties.Set(\"enabled\", &jsonschema.Schema{\n\t\tAnyOf: []*jsonschema.Schema{\n\t\t\t{Const: false},\n\t\t\t{Type: \"null\"},\n\t\t},\n\t})\n\n\tuseDiscoveryFalseOrNull := &jsonschema.Schema{Properties: orderedmap.New[string, *jsonschema.Schema]()}\n\tuseDiscoveryFalseOrNull.Properties.Set(\"use_discovery\", &jsonschema.Schema{\n\t\tAnyOf: []*jsonschema.Schema{\n\t\t\t{Const: false},\n\t\t\t{Type: \"null\"},\n\t\t},\n\t})\n\n\tendpointsRequired := &jsonschema.Schema{\n\t\tRequired: []string{\"authorization_endpoint\", \"token_endpoint\", \"userinfo_endpoint\"},\n\t}\n\n\tissuerRequired := &jsonschema.Schema{\n\t\tRequired: []string{\"issuer\"},\n\t}\n\n\tschema.If = enabledFalseOrNull\n\tschema.Then = &jsonschema.Schema{\n\t\tRequired: []string{},\n\t}\n\tschema.Else = &jsonschema.Schema{\n\t\tRequired: []string{\"display_name\", \"client_id\", \"secret\", \"scopes\"},\n\t\tIf:       useDiscoveryFalseOrNull,\n\t\tThen:     endpointsRequired,\n\t\tElse:     issuerRequired,\n\t}\n}\n\ntype ThirdPartyProviders struct {\n\t// `apple` contains the provider configuration for Apple.\n\tApple ThirdPartyProvider `yaml:\"apple\" json:\"apple,omitempty\" koanf:\"apple\"`\n\t// `discord` contains the provider configuration for Discord.\n\tDiscord ThirdPartyProvider `yaml:\"discord\" json:\"discord,omitempty\" koanf:\"discord\"`\n\t// `github` contains the provider configuration for GitHub.\n\tGitHub ThirdPartyProvider `yaml:\"github\" json:\"github,omitempty\" koanf:\"github\"`\n\t// `google` contains the provider configuration for Google.\n\tGoogle ThirdPartyProvider `yaml:\"google\" json:\"google,omitempty\" koanf:\"google\"`\n\t// `linkedin` contains the provider configuration for LinkedIn.\n\tLinkedIn ThirdPartyProvider `yaml:\"linkedin\" json:\"linkedin,omitempty\" koanf:\"linkedin\"`\n\t// `microsoft` contains the provider configuration for Microsoft.\n\tMicrosoft ThirdPartyProvider `yaml:\"microsoft\" json:\"microsoft,omitempty\" koanf:\"microsoft\"`\n\t//`facebook` contains the provider configuration for Facebook.\n\tFacebook ThirdPartyProvider `yaml:\"facebook\" json:\"facebook,omitempty\" koanf:\"facebook\"`\n}\n\nfunc (p *ThirdPartyProviders) Validate() error {\n\ts := structs.New(p)\n\tfor _, field := range s.Fields() {\n\t\tprovider := field.Value().(ThirdPartyProvider)\n\t\terr := provider.Validate()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: %w\", strings.ToLower(field.Name()), err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *ThirdPartyProviders) HasEnabled() bool {\n\ts := structs.New(p)\n\tfor _, field := range s.Fields() {\n\t\tprovider := field.Value().(ThirdPartyProvider)\n\t\tif provider.Enabled {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (p *ThirdPartyProviders) GetEnabled() []ThirdPartyProvider {\n\ts := structs.New(p)\n\tvar enabledProviders []ThirdPartyProvider\n\tfor _, field := range s.Fields() {\n\t\tprovider := field.Value().(ThirdPartyProvider)\n\t\tif provider.Enabled {\n\t\t\tenabledProviders = append(enabledProviders, provider)\n\t\t}\n\t}\n\n\treturn enabledProviders\n}\n\nfunc (p *ThirdPartyProviders) Get(provider string) *ThirdPartyProvider {\n\ts := structs.New(p)\n\tfor _, field := range s.Fields() {\n\t\tif strings.ToLower(field.Name()) == strings.ToLower(provider) {\n\t\t\tp := field.Value().(ThirdPartyProvider)\n\t\t\treturn &p\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype ThirdPartyProvider struct {\n\t// `allow_linking` indicates whether existing accounts can be automatically linked with this provider.\n\t//\n\t// Linking is based on matching one of the email addresses of an existing user account with the (primary)\n\t// email address of the third party provider account.\n\tAllowLinking bool `yaml:\"allow_linking\" json:\"allow_linking,omitempty\" koanf:\"allow_linking\" split_words:\"true\"`\n\t// `client_id` is the ID of the OAuth/OIDC client. Must be obtained from the provider.\n\t//\n\t// Required if the provider is `enabled`.\n\tClientID    string `yaml:\"client_id\" json:\"client_id,omitempty\" koanf:\"client_id\" split_words:\"true\"`\n\tDisplayName string `jsonschema:\"-\" yaml:\"-\" json:\"-\" koanf:\"-\"`\n\t// `enabled` determines whether this provider is enabled.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n\t// `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent.\n\t// Possible values are:\n\t// - login\n\t// - none\n\t// - consent\n\t// - select_account\n\t// Please note that not all providers support all values. Check the corresponding docs of the provider for supported values.\n\tPrompt string `yaml:\"prompt\" json:\"prompt,omitempty\" koanf:\"prompt\"`\n\t// `secret` is the client secret for the OAuth/OIDC client. Must be obtained from the provider.\n\t//\n\t// Required if the provider is `enabled`.\n\tSecret string `yaml:\"secret\" json:\"secret,omitempty\" koanf:\"secret\"`\n\t// `ID` is a unique name/slug/identifier for the provider. It is the lowercased key of the corresponding field\n\t// in ThirdPartyProviders. See also: CustomThirdPartyProvider.ID.\n\tID string `jsonschema:\"-\" yaml:\"-\" json:\"-\" koanf:\"-\"`\n}\n\nfunc (ThirdPartyProvider) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tschema.Title = \"provider\"\n\n\tenabledTrue := &jsonschema.Schema{Properties: orderedmap.New[string, *jsonschema.Schema]()}\n\tenabledTrue.Properties.Set(\"enabled\", &jsonschema.Schema{Const: true})\n\n\tschema.If = enabledTrue\n\tschema.Then = &jsonschema.Schema{\n\t\tRequired: []string{\"client_id\", \"secret\"},\n\t}\n\tschema.Else = &jsonschema.Schema{\n\t\tRequired: []string{},\n\t}\n}\n\nfunc (p *ThirdPartyProvider) Validate() error {\n\tif p.Enabled {\n\t\tif p.ClientID == \"\" {\n\t\t\treturn errors.New(\"missing client ID\")\n\t\t}\n\t\tif p.Secret == \"\" {\n\t\t\treturn errors.New(\"missing client secret\")\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_username.go",
    "content": "package config\n\ntype Username struct {\n\t// `acquire_on_login` determines whether users, provided that they do not already have set a username,\n\t//\tare prompted to provide a username on login.\n\tAcquireOnLogin bool `yaml:\"acquire_on_login\" json:\"acquire_on_login,omitempty\" koanf:\"acquire_on_login\" split_words:\"true\" jsonschema:\"default=true\"`\n\t// `acquire_on_registration` determines whether users are prompted to provide a username on registration.\n\tAcquireOnRegistration bool `yaml:\"acquire_on_registration\" json:\"acquire_on_registration,omitempty\" koanf:\"acquire_on_registration\" split_words:\"true\" jsonschema:\"default=true\"`\n\t// `enabled` determines whether users can set a unique username.\n\t//\n\t// Usernames can contain letters (a-z,A-Z), numbers (0-9), and underscores.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n\t// `max_length` specifies the maximum allowed length of a username.\n\tMaxLength int `yaml:\"max_length\" json:\"max_length,omitempty\" koanf:\"max_length\" jsonschema:\"default=32\"`\n\t// `min_length` specifies the minimum length of a username.\n\tMinLength int `yaml:\"min_length\" json:\"min_length,omitempty\" koanf:\"min_length\" split_words:\"true\" jsonschema:\"default=3\"`\n\t// `optional` determines whether users must provide a username when prompted. The username can only be changed but\n\t// not deleted if usernames are required (`optional: false`).\n\tOptional bool `yaml:\"optional\" json:\"optional,omitempty\" koanf:\"optional\" jsonschema:\"default=true\"`\n\t// `use_as_login_identifier` determines whether usernames, if enabled, can be used for logging in.\n\tUseAsLoginIdentifier bool `yaml:\"use_as_login_identifier\" json:\"use_as_login_identifier,omitempty\" koanf:\"use_as_login_identifier\" jsonschema:\"default=true\"`\n}\n"
  },
  {
    "path": "backend/config/config_webauthn.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\twebauthnLib \"github.com/go-webauthn/webauthn/webauthn\"\n)\n\n// WebauthnSettings defines the settings for the webauthn authentication mechanism\ntype WebauthnSettings struct {\n\tRelyingParty RelyingParty `yaml:\"relying_party\" json:\"relying_party,omitempty\" koanf:\"relying_party\" split_words:\"true\" jsonschema:\"title=relying_party\"`\n\t// Deprecated, use `timeouts` instead.\n\tTimeout int `yaml:\"timeout\" json:\"timeout,omitempty\" koanf:\"timeout\" jsonschema:\"default=60000\"`\n\t// `timeouts` specifies the timeouts for passkey/WebAuthn registration and login.\n\tTimeouts WebauthnTimeouts `yaml:\"timeouts\" json:\"timeouts,omitempty\" koanf:\"timeouts\" split_words:\"true\" jsonschema:\"title=timeouts\"`\n\t// Deprecated, use `passkey.user_verification` instead\n\tUserVerification string                `yaml:\"user_verification\" json:\"user_verification,omitempty\" koanf:\"user_verification\" split_words:\"true\" jsonschema:\"default=preferred,enum=required,enum=preferred,enum=discouraged\"`\n\tHandler          *webauthnLib.WebAuthn `jsonschema:\"-\"`\n}\n\n// Validate does not need to validate the config, because the library does this already\nfunc (r *WebauthnSettings) Validate() error {\n\tvalidUv := []string{\"required\", \"preferred\", \"discouraged\"}\n\tif !slices.Contains(validUv, r.UserVerification) {\n\t\treturn fmt.Errorf(\"expected user_verification to be one of [%s], got: '%s'\", strings.Join(validUv, \", \"), r.UserVerification)\n\t}\n\treturn nil\n}\n\nfunc (r *WebauthnSettings) PostProcess() error {\n\trequireResidentKey := false\n\n\tconfig := &webauthnLib.Config{\n\t\tRPID:                  r.RelyingParty.Id,\n\t\tRPDisplayName:         r.RelyingParty.DisplayName,\n\t\tRPOrigins:             r.RelyingParty.Origins,\n\t\tAttestationPreference: protocol.PreferNoAttestation,\n\t\tAuthenticatorSelection: protocol.AuthenticatorSelection{\n\t\t\tRequireResidentKey: &requireResidentKey,\n\t\t\tResidentKey:        protocol.ResidentKeyRequirementDiscouraged,\n\t\t\tUserVerification:   protocol.VerificationRequired,\n\t\t},\n\t\tDebug: false,\n\t\tTimeouts: webauthnLib.TimeoutsConfig{\n\t\t\tLogin: webauthnLib.TimeoutConfig{\n\t\t\t\tEnforce: true,\n\t\t\t\tTimeout: time.Duration(r.Timeouts.Login) * time.Millisecond,\n\t\t\t},\n\t\t\tRegistration: webauthnLib.TimeoutConfig{\n\t\t\t\tEnforce: true,\n\t\t\t\tTimeout: time.Duration(r.Timeouts.Registration) * time.Millisecond,\n\t\t\t},\n\t\t},\n\t}\n\n\thandler, err := webauthnLib.New(config)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tr.Handler = handler\n\n\treturn nil\n}\n\n// RelyingParty webauthn settings for your application using hanko.\ntype RelyingParty struct {\n\t// `display_name` is the service's name that some WebAuthn Authenticators will display to the user during registration\n\t// and authentication ceremonies.\n\tDisplayName string `yaml:\"display_name\" json:\"display_name,omitempty\" koanf:\"display_name\" split_words:\"true\" jsonschema:\"default=Hanko Authentication Service\"`\n\tIcon        string `yaml:\"icon\" json:\"icon,omitempty\" koanf:\"icon\" jsonschema:\"-\"`\n\t// `id` is the [effective domain](https://html.spec.whatwg.org/multipage/browsers.html#concept-origin-effective-domain)\n\t// the passkey/WebAuthn credentials will be bound to.\n\tId string `yaml:\"id\" json:\"id,omitempty\" koanf:\"id\" jsonschema:\"default=localhost,examples=localhost,example.com,subdomain.example.com\"`\n\t// `origins` is a list of origins for which passkeys/WebAuthn credentials will be accepted by the server. Must\n\t// include the protocol and can only be the effective domain, or a registrable domain suffix of the effective\n\t// domain, as specified in the [`id`](#id). Except for `localhost`, the protocol **must** always be `https` for\n\t// passkeys/WebAuthn to work. IP Addresses will not work.\n\t//\n\t// For an Android application the origin must be the base64 url encoded SHA256 fingerprint of the signing\n\t// certificate.\n\tOrigins []string `yaml:\"origins\" json:\"origins,omitempty\" koanf:\"origins\" jsonschema:\"minItems=1,default=http://localhost:8888,examples=android:apk-key-hash:nLSu7wVTbnMOxLgC52f2faTnvCbXQrUn_wF9aCrr-l0,https://login.example.com\"`\n}\n\ntype WebauthnTimeouts struct {\n\t// `registration` determines the time, in milliseconds, that the client is willing to wait for the credential\n\t// creation request to the WebAuthn API to complete.\n\tRegistration int `yaml:\"registration\" json:\"registration,omitempty\" koanf:\"registration\" jsonschema:\"default=600000\"`\n\t// `login` determines the time, in milliseconds, that the client is willing to wait for the credential\n\t//  request to the WebAuthn API to complete.\n\tLogin int `yaml:\"login\" json:\"login,omitempty\" koanf:\"login\" jsonschema:\"default=600000\"`\n}\n"
  },
  {
    "path": "backend/config/config_webhook.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/invopop/jsonschema\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype WebhookSettings struct {\n\t// `allow_time_expiration` determines whether webhooks are disabled when unused for 30 days\n\t// (only for database webhooks).\n\tAllowTimeExpiration bool `yaml:\"allow_time_expiration\" json:\"allow_time_expiration,omitempty\" koanf:\"allow_time_expiration\" jsonschema:\"default=false\"`\n\t// `enabled` enables the webhook feature.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n\t// `hooks` is a list of Webhook configurations.\n\t//\n\t// When using environment variables the value for the `WEBHOOKS_HOOKS` key must be specified in the following\n\t// format:\n\t// `{\"callback\":\"http://app.com/usercb\",\"events\":[\"user\"]};{\"callback\":\"http://app.com/emailcb\",\"events\":[\"email.send\"]}`\n\tHooks Webhooks `yaml:\"hooks\" json:\"hooks,omitempty\" koanf:\"hooks\" jsonschema:\"title=hooks\"`\n}\n\nfunc (ws *WebhookSettings) Validate() error {\n\tif ws.Enabled {\n\t\tfor _, hook := range ws.Hooks {\n\t\t\terr := hook.Validate()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype Webhooks []Webhook\n\n// Decode is an implementation of the envconfig.Decoder interface.\n// Assumes that environment variables (for the WEBHOOKS_HOOKS key) have the following format:\n// {\"callback\":\"http://app.com/usercb\",\"events\":[\"user\"]};{\"callback\":\"http://app.com/emailcb\",\"events\":[\"email.send\"]}\nfunc (wd *Webhooks) Decode(value string) error {\n\twebhooks := Webhooks{}\n\thooks := strings.Split(value, \";\")\n\tfor _, hook := range hooks {\n\t\twebhook := Webhook{}\n\t\terr := json.Unmarshal([]byte(hook), &webhook)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid map json: %w\", err)\n\t\t}\n\t\twebhooks = append(webhooks, webhook)\n\n\t}\n\t*wd = webhooks\n\treturn nil\n}\n\ntype Webhook struct {\n\t// `callback` specifies the URL to which the change data will be sent.\n\tCallback string `yaml:\"callback\" json:\"callback,omitempty\" koanf:\"callback\"`\n\t// `events` is a list of events this hook listens for.\n\tEvents events.Events `yaml:\"events\" json:\"events,omitempty\" koanf:\"events\" jsonschema:\"title=events\"`\n}\n\nfunc (Webhook) JSONSchemaExtend(schema *jsonschema.Schema) {\n\tschema.Title = \"hooks\"\n\tevts, _ := schema.Properties.Get(\"events\")\n\n\t// If the jsonschema.Reflector is configured with the DoNotReference option set to true, then the items property\n\t// in the schema is nil, hence we simply create a jsonschema.Schema manually, otherwise we'd get a nil pointer\n\t// exception.\n\tif evts.Items == nil {\n\t\tevts.Items = &jsonschema.Schema{Type: \"string\"}\n\t}\n\tevts.Items.Title = \"events\"\n\tevts.Items.Enum = []any{\n\t\t\"user\",\n\t\t\"user.create\",\n\t\t\"user.delete\",\n\t\t\"user.login\",\n\t\t\"user.update\",\n\t\t\"user.update.email\",\n\t\t\"user.update.email.create\",\n\t\t\"user.update.email.delete\",\n\t\t\"user.update.email.primary\",\n\t\t\"user.update.password.update\",\n\t\t\"user.update.username\",\n\t\t\"user.update.username.create\",\n\t\t\"user.update.username.delete\",\n\t\t\"user.update.username.update\",\n\t\t\"email.send\",\n\t}\n\tevts.Items.Extras = map[string]any{\"meta:enum\": map[string]string{\n\t\t\"user\":                        \"Triggers on: user creation, user deletion, user update, email creation, email deletion, change of primary email\",\n\t\t\"user.create\":                 \"Triggers on: user creation\",\n\t\t\"user.delete\":                 \"Triggers on: user deletion\",\n\t\t\"user.login\":                  \"Triggers on: user login\",\n\t\t\"user.update\":                 \"Triggers on: user update, email creation, email deletion, change of primary email\",\n\t\t\"user.update.email\":           \"Triggers on: email creation, email deletion, change of primary email\",\n\t\t\"user.update.email.create\":    \"Triggers on: email creation\",\n\t\t\"user.update.email.delete\":    \"Triggers on: email deletion\",\n\t\t\"user.update.email.primary\":   \"Triggers on: change of primary email\",\n\t\t\"user.update.password.update\": \"Triggers on: change of password\",\n\t\t\"user.update.username\":        \"Triggers on: username creation, username deletion, change of username\",\n\t\t\"user.update.username.create\": \"Triggers on: username creation\",\n\t\t\"user.update.username.delete\": \"Triggers on: username deletion\",\n\t\t\"user.update.username.update\": \"Triggers on: change of username\",\n\t\t\"email.send\":                  \"Triggers on: an email was sent or should be sent\",\n\t}}\n}\n\nfunc (w *Webhook) Validate() error {\n\t_, err := url.Parse(w.Callback)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"callback is not a valid URL: %w\", err)\n\t}\n\n\tif len(w.Events) > 0 {\n\t\tfor i, e := range w.Events {\n\t\t\tisValid := events.IsValidEvent(e)\n\t\t\tif !isValid {\n\t\t\t\treturn fmt.Errorf(\"event '%s' at events[%d] is not a valid webhook event\", e, i)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/config/config_webhook_test.go",
    "content": "package config\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestWebhooks_Decode(t *testing.T) {\n\twebhooks := Webhooks{}\n\tvalue := \"{\\\"callback\\\":\\\"http://app.com/usercb\\\",\\\"events\\\":[\\\"user\\\"]};{\\\"callback\\\":\\\"http://app.com/callback\\\",\\\"events\\\":[\\\"email.send\\\"]}\"\n\terr := webhooks.Decode(value)\n\n\tassert.NoError(t, err)\n\tassert.Len(t, webhooks, 2, \"has 2 elements\")\n\tfor _, webhook := range webhooks {\n\t\tassert.IsType(t, Webhook{}, webhook)\n\t}\n}\n"
  },
  {
    "path": "backend/config/minimal-config.yaml",
    "content": "smtp:\n    port: \"465\"\n    host: smtp.example.com\n    user: example\n    password: example\ndatabase:\n  url: postgres://postgres:123456@127.0.0.1:5432/dummy\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\n\n"
  },
  {
    "path": "backend/config/passcode-smtp-config.yaml",
    "content": "database:\n  user: hanko\n  password: hanko\n  host: localhost\n  port: 5432\n  dialect: postgres\npasscode:\n  email:\n    from_address: no-reply@hanko.io\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\nemail_delivery:\n  enabled: true\nconvert_legacy_config: true\n"
  },
  {
    "path": "backend/config/root-passcode-smtp-config.yaml",
    "content": "database:\n  user: hanko\n  password: hanko\n  host: localhost\n  port: 5432\n  dialect: postgres\nsmtp:\n    host: smtp1.example.com\n    user: example1\n    password: example1\npasscode:\n  email:\n    from_address: no-reply@hanko.io\n  smtp:\n    host: smtp2.example.com\n    user: example2\n    password: example2\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\n"
  },
  {
    "path": "backend/config/security-notifications-disabled-config.yaml",
    "content": "smtp:\n    port: \"465\"\n    host: smtp.example.com\n    user: example\n    password: example\ndatabase:\n  url: postgres://postgres:123456@127.0.0.1:5432/dummy\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\nsecurity_notifications:\n  notifications:\n    email_create:\n      enabled: false\n    email_delete:\n      enabled: false\n    password_update:\n      enabled: false\n    passkey_create:\n      enabled: false\n    primary_email_update:\n      enabled: false\n    mfa_create:\n      enabled: false\n    mfa_delete:\n      enabled: false"
  },
  {
    "path": "backend/crypto/aes_gcm/aes_gcm.go",
    "content": "package aes_gcm\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// AESGCM is used to en-/decrypt the generated jwks with AES-GCM\ntype AESGCM struct {\n\tkeys [][32]byte\n}\n\n// Construct a AES GCM encrypter/decrypter and check the keys as a prerequisite\nfunc NewAESGCM(keys []string) (*AESGCM, error) {\n\tif len(keys) < 1 {\n\t\treturn nil, errors.New(\"at least one encryption key must be provided\")\n\t}\n\thashedKeys := [][32]byte{}\n\n\tfor i, v := range keys {\n\t\tif len(v) < 16 {\n\t\t\treturn nil, fmt.Errorf(\"secret Nr. %v is too short. It is %v but needs to be at least 16\", i, len(v))\n\t\t} else {\n\t\t\thashedKeys = append(hashedKeys, hashSecret(v))\n\t\t}\n\t}\n\n\treturn &AESGCM{keys: hashedKeys}, nil\n}\n\n// hashSecret converts strings to fixed 32byte long AES keys\nfunc hashSecret(key string) (res [32]byte) {\n\tres = sha256.Sum256([]byte(key))\n\treturn res\n}\n\n// Encrypt encrypts some data with the first key in list and base64 encodes it for storage in database. mostly copy/pasted from https://github.com/gtank/cryptopasta/blob/master/encrypt.go\nfunc (a *AESGCM) Encrypt(plaintext []byte) (string, error) {\n\tblock, err := aes.NewCipher(a.keys[0][:])\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tnonce := make([]byte, gcm.NonceSize())\n\t_, err = io.ReadFull(rand.Reader, nonce)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tciphertext := gcm.Seal(nonce, nonce, plaintext, nil)\n\n\treturn base64.URLEncoding.EncodeToString(ciphertext), nil\n}\n\n// Decrypt tries to decrypt with every key in the list\nfunc (a *AESGCM) Decrypt(ciphertext string) (plaintext []byte, err error) {\n\tfor _, key := range a.keys {\n\t\tif plaintext, err = a.decrypt(ciphertext, key); err == nil {\n\t\t\treturn plaintext, nil\n\t\t}\n\t}\n\treturn nil, err\n}\n\nfunc (a *AESGCM) decrypt(ciphertext string, key [32]byte) ([]byte, error) {\n\traw, err := base64.URLEncoding.DecodeString(ciphertext)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tblock, err := aes.NewCipher(key[:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(raw) < gcm.NonceSize() {\n\t\treturn nil, errors.New(\"malformed ciphertext\")\n\t}\n\n\tplaintext, err := gcm.Open(nil,\n\t\traw[:gcm.NonceSize()],\n\t\traw[gcm.NonceSize():],\n\t\tnil,\n\t)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn plaintext, nil\n}\n"
  },
  {
    "path": "backend/crypto/aes_gcm/aes_gcm_test.go",
    "content": "package aes_gcm\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n)\nimport \"github.com/stretchr/testify/assert\"\n\nfunc TestNewEncryptionKey(t *testing.T) {\n\tkey1 := newEncryptionKey()\n\tkey2 := newEncryptionKey()\n\tassert.Equal(t, len(key1), 32)\n\tassert.Equal(t, len(key2), 32)\n\tassert.NotEqualf(t, key1, key2, \"two separate constructed keys should not be equal.\")\n}\n\nfunc TestNewAESGCM(t *testing.T) {\n\tfor k, c := range []struct {\n\t\tkeys  []string\n\t\tcheck func(aesgcm *AESGCM, err error)\n\t}{\n\t\t{\n\t\t\tkeys: []string{},\n\t\t\tcheck: func(aesgcm *AESGCM, err error) {\n\t\t\t\tassert.Error(t, err, \"empty key list should get rejected.\")\n\t\t\t\tassert.Nil(t, aesgcm)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: []string{\"too-short\"},\n\t\t\tcheck: func(aesgcm *AESGCM, err error) {\n\t\t\t\tassert.Error(t, err, \"too short key should get rejected.\")\n\t\t\t\tassert.Nil(t, aesgcm)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: []string{string(newEncryptionKey()[:]), \"too-short\"},\n\t\t\tcheck: func(aesgcm *AESGCM, err error) {\n\t\t\t\tassert.Error(t, err, \"too short key in any position should get rejected.\")\n\t\t\t\tassert.Nil(t, aesgcm)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: []string{string(newEncryptionKey()[:])},\n\t\t\tcheck: func(aesgcm *AESGCM, err error) {\n\t\t\t\tassert.NoError(t, err, \"Generated Key should be accepted\")\n\t\t\t\tassert.NotNil(t, aesgcm)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: []string{string(newEncryptionKey()[:]), string(newEncryptionKey()[:])},\n\t\t\tcheck: func(aesgcm *AESGCM, err error) {\n\t\t\t\tassert.NoError(t, err, \"two generated keys should be accepted\")\n\t\t\t\tassert.NotNil(t, aesgcm)\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tc.check(NewAESGCM(c.keys))\n\t\t})\n\t}\n}\n\nfunc TestAESGCM_EncryptDecrypt(t *testing.T) {\n\t// Encrypt\n\tplaintext := \"testTesttestTestTestTEST\"\n\taesgcm, err := NewAESGCM([]string{string(newEncryptionKey()[:])})\n\tassert.NoError(t, err)\n\tassert.NotNil(t, aesgcm)\n\tciphertext, err := aesgcm.Encrypt([]byte(plaintext))\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, ciphertext)\n\t//Decrypt\n\tplainAgain, err := aesgcm.Decrypt(ciphertext)\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(plainAgain), plaintext)\n}\n\nfunc TestAESGCM_Decrypt(t *testing.T) {\n\tplaintext := \"testTesttestTestTestTEST\"\n\tkey := []string{\"superSecureAndRandomlyCreatedKey\"}\n\tciphertext := \"1SiZZlBRnNM8P9xzppDI4n8YYVMG4Hf9UKNANqmxXtsxpHPt3fojlxRmwwOhkfQSCPooOA==\"\n\taesgcm, err := NewAESGCM(key)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, aesgcm)\n\tdecrypted, err := aesgcm.Decrypt(ciphertext)\n\tassert.NoError(t, err)\n\tassert.Equal(t, []byte(plaintext), decrypted)\n}\n\nfunc TestAESGCM_SomeoneModifiedTheCiphertext(t *testing.T) {\n\t// Encrypt\n\tplaintext := \"testTesttestTestTestTEST\"\n\taesgcm, err := NewAESGCM([]string{string(newEncryptionKey()[:])})\n\tassert.NoError(t, err)\n\tassert.NotNil(t, aesgcm)\n\tciphertext, err := aesgcm.Encrypt([]byte(plaintext))\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, ciphertext)\n\n\t// Modify cipher\n\tcipher := []rune(ciphertext)\n\tcipher[35] = cipher[35] + 1\n\n\t//Try to decrypt\n\tplainAgain, err := aesgcm.Decrypt(string(cipher))\n\tassert.Error(t, err)\n\tassert.NotEqual(t, string(plainAgain), plaintext)\n}\n\n// newEncryptionKey generates a random 256-bit key. It panics if the source of randomness fails.\nfunc newEncryptionKey() *[32]byte {\n\tkey := [32]byte{}\n\t_, err := io.ReadFull(rand.Reader, key[:])\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &key\n}\n"
  },
  {
    "path": "backend/crypto/jwk/aws_kms/adapter.go",
    "content": "package aws_kms\n\nimport (\n\t\"context\"\n\t\"crypto\"\n\t\"crypto/rsa\"\n\t\"crypto/sha256\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\tawsConfig \"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/aws/aws-sdk-go-v2/service/kms\"\n\t\"github.com/aws/aws-sdk-go-v2/service/kms/types\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n)\n\n// AWSKMSAdapter implements crypto.Signer using AWS KMS\ntype AWSKMSAdapter struct {\n\tKeyId     string\n\tKmsClient *kms.Client\n\tpublicKey crypto.PublicKey // cache the public key\n\tkeyMutex  sync.RWMutex     // protect the cached key\n}\n\n// NewAWSKMSAdapter initializes and returns a new instance of AWSKMSAdapter with the provided KeyManagement configuration.\nfunc NewAWSKMSAdapter(ctx context.Context, cfg config.KeyManagement) (*AWSKMSAdapter, error) {\n\t// LoadDefaultConfig resolves AWS credentials using the following chain (in order of precedence):\n\t// 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)\n\t// 2. Shared credentials file (~/.aws/credentials)\n\t// 3. Shared config file (~/.aws/config)\n\t// 4. IAM role for Amazon EC2 (via instance metadata service)\n\t// 5. IAM role for Amazon ECS (via container credentials)\n\t// 6. IAM role for Amazon EKS (via service account token)\n\tawsCfg, err := awsConfig.LoadDefaultConfig(\n\t\tctx,\n\t\tawsConfig.WithRegion(cfg.Region),\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load aws config: %w\", err)\n\t}\n\n\tsvc := kms.NewFromConfig(awsCfg)\n\n\treturn &AWSKMSAdapter{\n\t\tKeyId:     cfg.KeyID,\n\t\tKmsClient: svc,\n\t}, nil\n}\n\nfunc (k *AWSKMSAdapter) Public() crypto.PublicKey {\n\tk.keyMutex.RLock()\n\tif k.publicKey != nil {\n\t\tdefer k.keyMutex.RUnlock()\n\t\treturn k.publicKey\n\t}\n\tk.keyMutex.RUnlock()\n\n\tk.keyMutex.Lock()\n\tdefer k.keyMutex.Unlock()\n\n\t// Double-check after acquiring write lock\n\tif k.publicKey != nil {\n\t\treturn k.publicKey\n\t}\n\n\t// Fetch and cache the public key\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\tresult, err := k.KmsClient.GetPublicKey(ctx, &kms.GetPublicKeyInput{\n\t\tKeyId: &k.KeyId,\n\t})\n\tif err != nil {\n\t\tlog.Printf(\"failed to get public key: %v\", err)\n\t\treturn nil\n\t}\n\n\tpubKey, err := x509.ParsePKIXPublicKey(result.PublicKey)\n\tif err != nil {\n\t\tlog.Printf(\"failed to parse public key: %v\", err)\n\t\treturn nil\n\t}\n\n\tk.publicKey = pubKey\n\treturn pubKey\n}\n\n// Sign implements crypto.Signer interface\nfunc (k *AWSKMSAdapter) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {\n\t// Note: rand parameter is not used because AWS KMS handles randomness internally\n\t_ = rand // explicitly ignore to show intent\n\n\t// Determine the signing algorithm based on the hash function\n\tvar signingAlgorithm types.SigningAlgorithmSpec\n\tswitch opts.HashFunc() {\n\tcase crypto.SHA256:\n\t\tsigningAlgorithm = types.SigningAlgorithmSpecRsassaPkcs1V15Sha256 // or RsassaPkcs1V15Sha256\n\tcase crypto.SHA384:\n\t\tsigningAlgorithm = types.SigningAlgorithmSpecRsassaPkcs1V15Sha384\n\tcase crypto.SHA512:\n\t\tsigningAlgorithm = types.SigningAlgorithmSpecRsassaPkcs1V15Sha512\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported hash function: %v\", opts.HashFunc())\n\t}\n\n\t// Sign the digest using AWS KMS\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\tresult, err := k.KmsClient.Sign(ctx, &kms.SignInput{\n\t\tKeyId:            &k.KeyId,\n\t\tMessage:          digest,\n\t\tMessageType:      types.MessageTypeDigest,\n\t\tSigningAlgorithm: signingAlgorithm,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to sign with AWS KMS: %w\", err)\n\t}\n\n\treturn result.Signature, nil\n}\n\n// Verify verifies a signature using the AWS KMS public key\nfunc (k *AWSKMSAdapter) Verify(message, signature []byte, algorithm jwa.SignatureAlgorithm) error {\n\t// Get the public key\n\tpubKey := k.Public()\n\tif pubKey == nil {\n\t\treturn fmt.Errorf(\"failed to get public key\")\n\t}\n\n\t// Verify the signature directly using crypto operations\n\tswitch algorithm {\n\tcase jwa.RS256:\n\t\trsaPubKey, ok := pubKey.(*rsa.PublicKey)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected RSA public key for RS256\")\n\t\t}\n\t\thash := sha256.Sum256(message)\n\t\treturn rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, hash[:], signature)\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported algorithm: %s\", algorithm)\n\t}\n}\n"
  },
  {
    "path": "backend/crypto/jwk/aws_kms/manager.go",
    "content": "package aws_kms\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jws\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n)\n\n// AWSKMSManager implements the KeyManager interface using AWS KMS\ntype AWSKMSManager struct {\n\tawsAdapter *AWSKMSAdapter\n\talgorithm  jwa.SignatureAlgorithm\n}\n\nfunc NewAWSKMSManager(cfg config.KeyManagement) (*AWSKMSManager, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\tadapter, err := NewAWSKMSAdapter(ctx, cfg)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create AWS KMS adapter: %w\", err)\n\t}\n\n\treturn &AWSKMSManager{\n\t\tawsAdapter: adapter,\n\t\talgorithm:  jwa.RS256,\n\t}, nil\n}\n\nfunc (m *AWSKMSManager) GenerateKey() (jwk.Key, error) {\n\treturn nil, fmt.Errorf(\"not implemented\")\n}\n\nfunc (m *AWSKMSManager) GetPublicKeys() (jwk.Set, error) {\n\tpublicKey := m.awsAdapter.Public()\n\tkey, err := jwk.PublicKeyOf(publicKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to convert public key: %w\", err)\n\t}\n\n\tif err := key.Set(jwk.KeyIDKey, m.awsAdapter.KeyId); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to set key id: %w\", err)\n\t}\n\n\tif err := key.Set(jwk.KeyUsageKey, jwk.ForSignature); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to set key usage: %w\", err)\n\t}\n\n\tif err := key.Set(jwk.AlgorithmKey, m.algorithm); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to set algorithm: %w\", err)\n\t}\n\n\tset := jwk.NewSet()\n\tif err := set.AddKey(key); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to add key to set: %w\", err)\n\t}\n\n\treturn set, nil\n}\n\nfunc (m *AWSKMSManager) GetSigningKey() (jwk.Key, error) {\n\treturn nil, fmt.Errorf(\"not implemented\")\n}\n\nfunc (m *AWSKMSManager) Sign(token jwt.Token) ([]byte, error) {\n\theaders := jws.NewHeaders()\n\t_ = headers.Set(jws.KeyIDKey, m.awsAdapter.KeyId)\n\n\t// Use the adapter struct for signing\n\tsigned, err := jwt.Sign(token, jwt.WithKey(m.algorithm, m.awsAdapter, jws.WithProtectedHeaders(headers)))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to sign JWT with AWS KMS: %w\", err)\n\t}\n\n\treturn signed, nil\n}\n\nfunc (m *AWSKMSManager) Verify(bytes []byte) (jwt.Token, error) {\n\t// Use the adapter struct for verification\n\ttoken, err := jwt.Parse(bytes, jwt.WithKey(m.algorithm, m.awsAdapter))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to verify JWT with AWS KMS: %w\", err)\n\t}\n\n\treturn token, nil\n}\n"
  },
  {
    "path": "backend/crypto/jwk/local_db/generator.go",
    "content": "package local_db\n\nimport \"github.com/lestrrat-go/jwx/v2/jwk\"\n\n// KeyGenerator Interface for JSON Web Key Generation\ntype KeyGenerator interface {\n\t// Generate a new JWK with a given id\n\tGenerate(id string) (jwk.Key, error)\n}\n"
  },
  {
    "path": "backend/crypto/jwk/local_db/generator_rsa.go",
    "content": "package local_db\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n)\n\n// RSAKeyGenerator\ntype RSAKeyGenerator struct {\n}\n\nfunc (g *RSAKeyGenerator) Generate(id string) (jwk.Key, error) {\n\trawKey, err := rsa.GenerateKey(rand.Reader, 4096)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if err = rawKey.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey, err := jwk.FromRaw(rawKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = key.Set(jwk.KeyIDKey, id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = key.Set(jwk.AlgorithmKey, jwa.RS256)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = key.Set(jwk.KeyUsageKey, jwk.ForSignature)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn key, nil\n}\n"
  },
  {
    "path": "backend/crypto/jwk/local_db/generator_test.go",
    "content": "package local_db\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"testing\"\n)\n\nfunc TestGenerator(t *testing.T) {\n\tfor k, c := range []struct {\n\t\tg     KeyGenerator\n\t\tname  string\n\t\tcheck func(ks jwk.Key)\n\t}{\n\t\t{\n\t\t\tg:    &RSAKeyGenerator{},\n\t\t\tname: \"generate_rsa_jwk\",\n\t\t\tcheck: func(ks jwk.Key) {\n\t\t\t\t//assert.Len(t, ks, 2)\n\t\t\t\trsaKey, ok := (ks).(jwk.RSAPrivateKey)\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Fail()\n\t\t\t\t}\n\t\t\t\tkeyId, _ := rsaKey.Get(jwk.KeyIDKey)\n\t\t\t\tassert.Equal(t, keyId, \"my_key_id\")\n\t\t\t\tassert.Equal(t, jwa.RSA, rsaKey.KeyType())\n\t\t\t\tbuf, err := json.MarshalIndent(rsaKey, \"\", \"  \")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tt.Logf(\"%s\\n\", buf)\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d - %v\", k, c.name), func(t *testing.T) {\n\t\t\tkeys, err := c.g.Generate(\"my_key_id\")\n\t\t\trequire.NoError(t, err)\n\t\t\tif err == nil {\n\t\t\t\tc.check(keys)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/crypto/jwk/local_db/manager.go",
    "content": "package local_db\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/aes_gcm\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype DefaultManager struct {\n\tencrypter *aes_gcm.AESGCM\n\tpersister persistence.JwkPersister\n}\n\n// NewDefaultManager creates a DefaultManager that reads and persists private keys to the database and generates new private keys when a new secret is added to the config.\n// It manages the lifecycle of JSON Web Keys, handling encryption, persistence and retrieval.\nfunc NewDefaultManager(keys []string, persister persistence.JwkPersister) (*DefaultManager, error) {\n\tencrypter, err := aes_gcm.NewAESGCM(keys)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmanager := &DefaultManager{\n\t\tencrypter: encrypter,\n\t\tpersister: persister,\n\t}\n\t// for every key we should check if a jwk with index exists and create one if not.\n\tfor i := range keys {\n\t\tj, err := persister.Get(i + 1)\n\t\tif j == nil && err == nil {\n\t\t\t_, err := manager.GenerateKey()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn manager, nil\n}\n\n// GenerateKey generates a new RSA key and persists it to the database\nfunc (m *DefaultManager) GenerateKey() (jwk.Key, error) {\n\trsa := &RSAKeyGenerator{}\n\tid, _ := uuid.NewV4()\n\tkey, err := rsa.Generate(id.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmarshalled, err := json.Marshal(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tencryptedKey, err := m.encrypter.Encrypt(marshalled)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmodel := models.Jwk{\n\t\tKeyData:   encryptedKey,\n\t\tCreatedAt: time.Now(),\n\t}\n\terr = m.persister.Create(model)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn key, nil\n}\n\n// GetSigningKey returns the private key used for signing\nfunc (m *DefaultManager) GetSigningKey() (jwk.Key, error) {\n\tsigModel, err := m.persister.GetLast()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tk, err := m.encrypter.Decrypt(sigModel.KeyData)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey, err := jwk.ParseKey(k)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn key, nil\n}\n\n// GetPublicKeys returns all public keys\nfunc (m *DefaultManager) GetPublicKeys() (jwk.Set, error) {\n\tmodelList, err := m.persister.GetAll()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpublicKeys := jwk.NewSet()\n\tfor _, model := range modelList {\n\t\tk, err := m.encrypter.Decrypt(model.KeyData)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tkey, err := jwk.ParseKey(k)\n\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tpublicKey, err := jwk.PublicKeyOf(key)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\terr = publicKeys.AddKey(publicKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn publicKeys, nil\n}\n\n// Sign a JWT with the signing key and returns it\nfunc (m *DefaultManager) Sign(token jwt.Token) ([]byte, error) {\n\tkey, err := m.GetSigningKey()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get signing key: %w\", err)\n\t}\n\tsigned, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, key))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to sign jwt: %w\", err)\n\t}\n\treturn signed, nil\n}\n\n// Verify verifies a JWT, using the verificationKeys and returns the parsed JWT\nfunc (m *DefaultManager) Verify(signed []byte) (jwt.Token, error) {\n\tkeys, err := m.GetPublicKeys()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get public keys: %w\", err)\n\t}\n\ttoken, err := jwt.Parse(signed, jwt.WithKeySet(keys))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to verify jwt: %w\", err)\n\t}\n\treturn token, nil\n}\n"
  },
  {
    "path": "backend/crypto/jwk/local_db/manager_test.go",
    "content": "package local_db\n\nimport (\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"testing\"\n)\n\nfunc TestJWKManagerSuite(t *testing.T) {\n\ts := new(jwkManagerSuite)\n\tsuite.Run(t, s)\n}\n\ntype jwkManagerSuite struct {\n\ttest.Suite\n}\n\nfunc (s *jwkManagerSuite) TestDefaultManager() {\n\tkeys := []string{\"asfnoadnfoaegnq3094intoaegjnoadjgnoadng\", \"apdisfoaiegnoaiegnbouaebgn982\"}\n\n\tpersister := s.Storage.GetJwkPersister()\n\n\tdm, err := NewDefaultManager(keys, persister)\n\trequire.NoError(s.T(), err)\n\tall, err := persister.GetAll()\n\n\trequire.NoError(s.T(), err)\n\tassert.Equal(s.T(), 2, len(all))\n\n\tjs, err := dm.GetPublicKeys()\n\trequire.NoError(s.T(), err)\n\tassert.Equal(s.T(), 2, js.Len())\n\n\tsk, err := dm.GetSigningKey()\n\trequire.NoError(s.T(), err)\n\n\ttoken := jwt.New()\n\ttoken.Set(\"Payload\", \"isJustFine\")\n\tsigned, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, sk))\n\trequire.NoError(s.T(), err)\n\n\t// Get Public Key of signing key\n\tpk, err := sk.PublicKey()\n\trequire.NoError(s.T(), err)\n\n\t// Parse and Verify\n\ttokenParsed, err := jwt.Parse(signed, jwt.WithKey(jwa.RS256, pk))\n\tassert.NoError(s.T(), err)\n\tassert.Equal(s.T(), token, tokenParsed)\n}\n"
  },
  {
    "path": "backend/crypto/jwk/manager.go",
    "content": "package jwk\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk/aws_kms\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk/local_db\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n)\n\nfunc NewManager(cfg config.Secrets, persister persistence.Persister) (KeyProvider, error) {\n\tswitch cfg.KeyManagement.Type {\n\tcase \"local\":\n\t\treturn local_db.NewDefaultManager(cfg.Keys, persister.GetJwkPersister())\n\tcase \"aws_kms\":\n\t\treturn aws_kms.NewAWSKMSManager(cfg.KeyManagement)\n\t}\n\n\treturn nil, fmt.Errorf(\"unsupported key management type: %s\", cfg.KeyManagement.Type)\n}\n"
  },
  {
    "path": "backend/crypto/jwk/types.go",
    "content": "package jwk\n\nimport (\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n)\n\n// KeyProvider combines all key management capabilities\ntype KeyProvider interface {\n\tManager\n\tGenerator\n}\n\ntype Manager interface {\n\t// GenerateKey is used to generate a jwk Key\n\tGenerateKey() (jwk.Key, error)\n\t// GetPublicKeys returns all Public keys that are persisted\n\tGetPublicKeys() (jwk.Set, error)\n\t// GetSigningKey returns the last added private key that is used for signing\n\tGetSigningKey() (jwk.Key, error)\n}\n\ntype Generator interface {\n\tSign(jwt.Token) ([]byte, error)\n\tVerify([]byte) (jwt.Token, error)\n}\n"
  },
  {
    "path": "backend/crypto/passcode.go",
    "content": "package crypto\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"math/big\"\n)\n\ntype PasscodeGenerator interface {\n\tGenerate() (string, error)\n}\n\ntype numericPasscodeGenerator struct {\n}\n\nfunc NewNumericPasscodeGenerator() PasscodeGenerator {\n\treturn &numericPasscodeGenerator{}\n}\n\nfunc (g *numericPasscodeGenerator) Generate() (string, error) {\n\tmax := big.NewInt(999999)\n\tn, err := rand.Int(rand.Reader, max)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to generate random number: %w\", err)\n\t}\n\treturn fmt.Sprintf(\"%06d\", n), nil\n}\n\ntype alphanumericPasscodeGenerator struct {\n}\n\n// alphanumericChars without ambiguous characters: 0 (zero), 1 (one), O (oh), I, (eye), l (ell)\nconst alphanumericChars = \"23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\"\n\nfunc NewAlphanumericPasscodeGenerator() PasscodeGenerator {\n\treturn &alphanumericPasscodeGenerator{}\n}\n\nfunc (a *alphanumericPasscodeGenerator) Generate() (string, error) {\n\tb := make([]byte, 6)\n\tmax := big.NewInt(int64(len(alphanumericChars)))\n\tfor i := range b {\n\t\tn, err := rand.Int(rand.Reader, max)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to generate random number: %w\", err)\n\t\t}\n\t\tb[i] = alphanumericChars[n.Int64()]\n\t}\n\treturn string(b), nil\n}\n"
  },
  {
    "path": "backend/crypto/passcode_test.go",
    "content": "package crypto\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPasscodeGenerator_Generate(t *testing.T) {\n\tpg := NewNumericPasscodeGenerator()\n\tpasscode, err := pg.Generate()\n\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, passcode)\n\tassert.Equal(t, 6, len(passcode))\n}\n\nfunc TestPasscodeGenerator_Generate_Different_Codes(t *testing.T) {\n\tpg := NewNumericPasscodeGenerator()\n\n\tpasscode1, err := pg.Generate()\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, passcode1)\n\n\tpasscode2, err := pg.Generate()\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, passcode2)\n\n\tassert.NotEqual(t, passcode1, passcode2)\n}\n\nfunc TestAlphanumericPasscodeGenerator_Generate(t *testing.T) {\n\tpg := NewAlphanumericPasscodeGenerator()\n\tpasscode, err := pg.Generate()\n\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, passcode)\n\tassert.Equal(t, 6, len(passcode))\n}\n\nfunc TestAlphanumericPasscodeGenerator_Generate_Different_Codes(t *testing.T) {\n\tpg := NewAlphanumericPasscodeGenerator()\n\n\tpasscode1, err := pg.Generate()\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, passcode1)\n\n\tpasscode2, err := pg.Generate()\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, passcode2)\n\n\tassert.NotEqual(t, passcode1, passcode2)\n}\n"
  },
  {
    "path": "backend/crypto/string.go",
    "content": "package crypto\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n)\n\n// GenerateRandomBytes returns securely generated random bytes.\n// It will return an error if the system's secure random\n// number generator fails to function correctly, in which\n// case the caller should not continue.\nfunc GenerateRandomBytes(n int) ([]byte, error) {\n\tb := make([]byte, n)\n\t_, err := rand.Read(b)\n\t// Note that err == nil only if we read len(b) bytes.\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn b, nil\n}\n\n// GenerateRandomStringURLSafe returns a URL-safe, base64 encoded\n// securely generated random string.\n// It will return an error if the system's secure random\n// number generator fails to function correctly, in which\n// case the caller should not continue.\nfunc GenerateRandomStringURLSafe(n int) (string, error) {\n\tb, err := GenerateRandomBytes(n)\n\treturn base64.URLEncoding.EncodeToString(b), err\n}\n"
  },
  {
    "path": "backend/dto/admin/email.go",
    "content": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\ntype Email struct {\n\tID         uuid.UUID `json:\"id\"`\n\tAddress    string    `json:\"address\"`\n\tIsVerified bool      `json:\"is_verified\"`\n\tIsPrimary  bool      `json:\"is_primary\"`\n\tCreatedAt  time.Time `json:\"created_at\"`\n\tUpdatedAt  time.Time `json:\"updated_at\"`\n}\n\n// FromEmailModel Converts the DB model to a DTO object\nfunc FromEmailModel(email *models.Email) *Email {\n\treturn &Email{\n\t\tID:         email.ID,\n\t\tAddress:    email.Address,\n\t\tIsVerified: email.Verified,\n\t\tIsPrimary:  email.IsPrimary(),\n\t\tCreatedAt:  email.CreatedAt,\n\t\tUpdatedAt:  email.UpdatedAt,\n\t}\n}\n\ntype CreateEmail struct {\n\tAddress    string `json:\"address\" validate:\"required,email\"`\n\tIsPrimary  bool   `json:\"is_primary\"`\n\tIsVerified bool   `json:\"is_verified\"`\n}\n\ntype EmailRequests interface {\n\tListEmailRequestDto | CreateEmailRequestDto | GetEmailRequestDto\n}\n\ntype ListEmailRequestDto struct {\n\tUserId string `param:\"user_id\" validate:\"required,uuid\"`\n}\n\ntype CreateEmailRequestDto struct {\n\tListEmailRequestDto\n\tCreateEmail\n}\n\ntype GetEmailRequestDto struct {\n\tListEmailRequestDto\n\tEmailId string `param:\"email_id\" validate:\"required,uuid4\"`\n}\n"
  },
  {
    "path": "backend/dto/admin/identity.go",
    "content": "package admin\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype Identity struct {\n\tID           uuid.UUID  `json:\"id\"`\n\tProviderID   string     `json:\"provider_id\"`\n\tProviderName string     `json:\"provider_name\"`\n\tEmailID      *uuid.UUID `json:\"email_id\"`\n\tCreatedAt    time.Time  `json:\"created_at\"`\n\tUpdatedAt    time.Time  `json:\"updated_at\"`\n}\n\nfunc FromIdentityModel(model models.Identity) Identity {\n\treturn Identity{\n\t\tID:           model.ID,\n\t\tProviderID:   model.ProviderUserID,\n\t\tProviderName: model.ProviderID,\n\t\tEmailID:      model.EmailID,\n\t\tCreatedAt:    model.CreatedAt,\n\t\tUpdatedAt:    model.UpdatedAt,\n\t}\n}\n"
  },
  {
    "path": "backend/dto/admin/metadata.go",
    "content": "package admin\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/tidwall/gjson\"\n\t\"strings\"\n)\n\ntype PatchMetadataRequest struct {\n\tMetadata gjson.Result\n}\n\nfunc (m *PatchMetadataRequest) UnmarshalJSON(data []byte) error {\n\tif !gjson.ValidBytes(data) {\n\t\treturn fmt.Errorf(\"body is not valid JSON\")\n\t}\n\n\tbody := gjson.GetBytes(data, \"@this\")\n\tif body.Raw == \"null\" {\n\t\tm.Metadata = body\n\t\treturn nil\n\t}\n\n\tif body.Raw == \"\" || (body.Raw != \"null\" && !body.IsObject()) {\n\t\treturn errors.New(\"patch metadata must be null or object\")\n\t}\n\n\tvar validResultKeys []string\n\tfor _, key := range []string{\"public_metadata\", \"private_metadata\", \"unsafe_metadata\"} {\n\t\tprop := gjson.GetBytes(data, key)\n\n\t\tif !prop.Exists() {\n\t\t\tcontinue\n\t\t}\n\n\t\tif prop.Raw != \"null\" && !prop.IsObject() {\n\t\t\treturn fmt.Errorf(\"%s must be an object or null\", key)\n\t\t}\n\n\t\tif prop.Raw != \"{}\" {\n\t\t\tvalidResultKeys = append(validResultKeys, key)\n\t\t}\n\t}\n\n\tm.Metadata = gjson.GetBytes(data, fmt.Sprintf(\"{%s}\", strings.Join(validResultKeys, \",\")))\n\n\treturn nil\n}\n\ntype Metadata struct {\n\tPublic  json.RawMessage `json:\"public_metadata,omitempty\"`\n\tPrivate json.RawMessage `json:\"private_metadata,omitempty\"`\n\tUnsafe  json.RawMessage `json:\"unsafe_metadata,omitempty\"`\n}\n\nfunc NewMetadata(metadata *models.UserMetadata) *Metadata {\n\tresult := &Metadata{}\n\n\tif metadata.Public.Valid && metadata.Public.String != \"{}\" {\n\t\tresult.Public = json.RawMessage(metadata.Public.String)\n\t}\n\tif metadata.Private.Valid && metadata.Private.String != \"{}\" {\n\t\tresult.Private = json.RawMessage(metadata.Private.String)\n\t}\n\tif metadata.Unsafe.Valid && metadata.Unsafe.String != \"{}\" {\n\t\tresult.Unsafe = json.RawMessage(metadata.Unsafe.String)\n\t}\n\n\tif result.Public == nil && result.Unsafe == nil && result.Private == nil {\n\t\treturn nil\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "backend/dto/admin/otp.go",
    "content": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype GetOTPRequestDto struct {\n\tUserID string `param:\"user_id\" validate:\"required,uuid\"`\n}\n\ntype OTPDto struct {\n\tID        uuid.UUID `json:\"id\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n"
  },
  {
    "path": "backend/dto/admin/password.go",
    "content": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype PasswordCredential struct {\n\tID        uuid.UUID `json:\"id\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\"`\n}\n\ntype GetPasswordCredentialRequestDto struct {\n\tUserID string `param:\"user_id\" validate:\"required,uuid\"`\n}\n\ntype CreateOrUpdatePasswordCredentialRequestDto struct {\n\tGetPasswordCredentialRequestDto\n\tPassword string `json:\"password\" validate:\"required\"`\n}\n"
  },
  {
    "path": "backend/dto/admin/session.go",
    "content": "package admin\n\ntype CreateSessionTokenDto struct {\n\tUserID    string `json:\"user_id\" validate:\"required,uuid\"`\n\tUserAgent string `json:\"user_agent\"`\n\tIpAddress string `json:\"ip_address\" validate:\"omitempty,ip\"`\n}\n\ntype CreateSessionTokenResponse struct {\n\tSessionToken string `json:\"session_token\"`\n}\n\ntype ListSessionsRequestDto struct {\n\tUserID string `param:\"user_id\" validate:\"required,uuid\"`\n}\n\ntype DeleteSessionRequestDto struct {\n\tListSessionsRequestDto\n\tSessionID string `param:\"session_id\" validate:\"required,uuid4\"`\n}\n"
  },
  {
    "path": "backend/dto/admin/user.go",
    "content": "package admin\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype User struct {\n\tID                  uuid.UUID                        `json:\"id\"`\n\tWebauthnCredentials []dto.WebauthnCredentialResponse `json:\"webauthn_credentials,omitempty\"`\n\tEmails              []Email                          `json:\"emails,omitempty\"`\n\tUsername            *Username                        `json:\"username,omitempty\"`\n\tCreatedAt           time.Time                        `json:\"created_at\"`\n\tUpdatedAt           time.Time                        `json:\"updated_at\"`\n\tPassword            *PasswordCredential              `json:\"password,omitempty\"`\n\tIdentities          []Identity                       `json:\"identities,omitempty\"`\n\tOTP                 *OTPDto                          `json:\"otp,omitempty\"`\n\tIPAddress           *string                          `json:\"ip_address,omitempty\"`\n\tUserAgent           *string                          `json:\"user_agent,omitempty\"`\n\tMetadata            *Metadata                        `json:\"metadata,omitempty\"`\n\tGivenName           string                           `json:\"given_name,omitempty\"`\n\tFamilyName          string                           `json:\"family_name,omitempty\"`\n\tName                string                           `json:\"name,omitempty\"`\n\tPicture             string                           `json:\"picture,omitempty\"`\n}\n\nfunc (u *User) SetIPAddress(ip string) {\n\tu.IPAddress = &ip\n}\n\nfunc (u *User) SetUserAgent(agent string) {\n\tu.UserAgent = &agent\n}\n\n// FromUserModel Converts the DB model to a DTO object\nfunc FromUserModel(model models.User) User {\n\tcredentials := make([]dto.WebauthnCredentialResponse, len(model.WebauthnCredentials))\n\tfor i := range model.WebauthnCredentials {\n\t\tcredentials[i] = *dto.FromWebauthnCredentialModel(&model.WebauthnCredentials[i])\n\t}\n\temails := make([]Email, len(model.Emails))\n\tvar identities = make([]Identity, 0)\n\tfor i := range model.Emails {\n\t\temails[i] = *FromEmailModel(&model.Emails[i])\n\t\tfor j := range model.Emails[i].Identities {\n\t\t\tidentities = append(identities, FromIdentityModel(model.Emails[i].Identities[j]))\n\t\t}\n\t}\n\tvar username *Username = nil\n\tif model.Username != nil {\n\t\tusername = FromUsernameModel(model.Username)\n\t}\n\n\tvar passwordCredential *PasswordCredential = nil\n\tif model.PasswordCredential != nil {\n\t\tpasswordCredential = &PasswordCredential{\n\t\t\tID:        model.PasswordCredential.ID,\n\t\t\tCreatedAt: model.PasswordCredential.CreatedAt,\n\t\t\tUpdatedAt: model.PasswordCredential.UpdatedAt,\n\t\t}\n\t}\n\n\tvar otp *OTPDto = nil\n\tif model.OTPSecret != nil {\n\t\totp = &OTPDto{\n\t\t\tID:        model.OTPSecret.ID,\n\t\t\tCreatedAt: model.OTPSecret.CreatedAt,\n\t\t}\n\t}\n\n\tvar metadata *Metadata\n\tif model.Metadata != nil {\n\t\tmetadata = NewMetadata(model.Metadata)\n\t}\n\n\treturn User{\n\t\tID:                  model.ID,\n\t\tWebauthnCredentials: credentials,\n\t\tEmails:              emails,\n\t\tUsername:            username,\n\t\tCreatedAt:           model.CreatedAt,\n\t\tUpdatedAt:           model.UpdatedAt,\n\t\tPassword:            passwordCredential,\n\t\tIdentities:          identities,\n\t\tOTP:                 otp,\n\t\tMetadata:            metadata,\n\t\tGivenName:           model.GivenName.String,\n\t\tFamilyName:          model.FamilyName.String,\n\t\tName:                model.Name.String,\n\t\tPicture:             model.Picture.String,\n\t}\n}\n\ntype CreateUser struct {\n\tID        uuid.UUID     `json:\"id\"`\n\tEmails    []CreateEmail `json:\"emails\" validate:\"unique=Address,dive\"`\n\tUsername  *string       `json:\"username\"`\n\tCreatedAt time.Time     `json:\"created_at\"`\n}\n"
  },
  {
    "path": "backend/dto/admin/username.go",
    "content": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\ntype Username struct {\n\tID        uuid.UUID `json:\"id\"`\n\tUsername  string    `json:\"username\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\"`\n}\n\n// FromEmailModel Converts the DB model to a DTO object\nfunc FromUsernameModel(model *models.Username) *Username {\n\treturn &Username{\n\t\tID:        model.ID,\n\t\tUsername:  model.Username,\n\t\tCreatedAt: model.CreatedAt,\n\t\tUpdatedAt: model.UpdatedAt,\n\t}\n}\n"
  },
  {
    "path": "backend/dto/admin/webauthn.go",
    "content": "package admin\n\ntype ListWebauthnCredentialsRequestDto struct {\n\tUserID string `param:\"user_id\" validate:\"required,uuid\"`\n}\n\ntype GetWebauthnCredentialRequestDto struct {\n\tListWebauthnCredentialsRequestDto\n\tWebauthnCredentialID string `param:\"credential_id\" validate:\"required\"`\n}\n"
  },
  {
    "path": "backend/dto/admin/webhook.go",
    "content": "package admin\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n)\n\ntype WebhookListResponseDto struct {\n\tDatabase models.Webhooks `json:\"database\"`\n\tConfig   config.Webhooks `json:\"config\"`\n}\n\ntype CreateWebhookRequestDto struct {\n\tCallback string        `json:\"callback\" validate:\"required,url\"`\n\tEvents   events.Events `json:\"events\" validate:\"required,min=1,dive,hanko_event\"`\n}\n\ntype GetWebhookRequestDto struct {\n\tID string `param:\"id\" validate:\"required,uuid4\"`\n}\n\ntype UpdateWebhookRequestDto struct {\n\tGetWebhookRequestDto\n\tCreateWebhookRequestDto\n\tEnabled bool `json:\"enabled\" validate:\"required,boolean\"`\n}\n"
  },
  {
    "path": "backend/dto/config.go",
    "content": "package dto\n\nimport (\n\t\"github.com/fatih/structs\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\tsamlConfig \"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n)\n\n// PublicConfig is the part of the configuration that will be shared with the frontend\ntype PublicConfig struct {\n\tPassword                Password `json:\"password\"`\n\tEmails                  Emails   `json:\"emails\"`\n\tProviders               []string `json:\"providers\"`\n\tAccount                 Account  `json:\"account\"`\n\tUseEnterpriseConnection bool     `json:\"use_enterprise\"`\n}\n\ntype Password struct {\n\tEnabled   bool `json:\"enabled\"`\n\tMinLength int  `json:\"min_password_length\"`\n}\n\ntype Emails struct {\n\tRequireVerification bool `json:\"require_verification\"`\n\tMaxNumOfAddresses   int  `json:\"max_num_of_addresses\"`\n}\n\ntype Account struct {\n\tAllowDeletion bool `json:\"allow_deletion\"`\n\tAllowSignup   bool `json:\"allow_signup\"`\n}\n\n// FromConfig Returns a PublicConfig from the Application configuration\nfunc FromConfig(cfg config.Config) PublicConfig {\n\treturn PublicConfig{\n\t\tPassword: Password{\n\t\t\tEnabled:   cfg.Password.Enabled,\n\t\t\tMinLength: cfg.Password.MinLength,\n\t\t},\n\t\tEmails: Emails{\n\t\t\tRequireVerification: cfg.Email.RequireVerification,\n\t\t\tMaxNumOfAddresses:   cfg.Email.Limit,\n\t\t},\n\t\tProviders: GetEnabledProviders(cfg.ThirdParty.Providers),\n\t\tAccount: Account{\n\t\t\tAllowDeletion: cfg.Account.AllowDeletion,\n\t\t\tAllowSignup:   cfg.Account.AllowSignup,\n\t\t},\n\t\tUseEnterpriseConnection: UseEnterpriseConnection(&cfg.Saml),\n\t}\n}\n\nfunc GetEnabledProviders(providers config.ThirdPartyProviders) []string {\n\ts := structs.New(providers)\n\tvar enabledProviders []string\n\tfor _, field := range s.Fields() {\n\t\tv := field.Value().(config.ThirdPartyProvider)\n\t\tif v.Enabled {\n\t\t\tenabledProviders = append(enabledProviders, field.Name())\n\t\t}\n\t}\n\treturn enabledProviders\n}\n\nfunc UseEnterpriseConnection(samlConfig *samlConfig.Saml) bool {\n\thasProvider := false\n\n\tif samlConfig != nil && samlConfig.Enabled {\n\t\tfor _, availableProvider := range samlConfig.IdentityProviders {\n\t\t\tif availableProvider.Enabled {\n\t\t\t\thasProvider = true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn hasProvider\n\n}\n"
  },
  {
    "path": "backend/dto/email.go",
    "content": "package dto\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype EmailResponse struct {\n\tID         uuid.UUID  `json:\"id\"`\n\tAddress    string     `json:\"address\"`\n\tIsVerified bool       `json:\"is_verified\"`\n\tIsPrimary  bool       `json:\"is_primary\"`\n\tIdentity   *Identity  `json:\"identity,omitempty\"`   // Deprecated\n\tIdentities Identities `json:\"identities,omitempty\"` // Deprecated. Identities are now accessible at the user level.\n}\n\ntype EmailCreateRequest struct {\n\tAddress string `json:\"address\"`\n}\n\ntype EmailUpdateRequest struct {\n\tIsPrimary *bool `json:\"is_primary\"`\n}\n\n// FromEmailModel Converts the DB model to a DTO object\nfunc FromEmailModel(email *models.Email, cfg *config.Config) *EmailResponse {\n\temailResponse := &EmailResponse{\n\t\tID:         email.ID,\n\t\tAddress:    email.Address,\n\t\tIsVerified: email.Verified,\n\t\tIsPrimary:  email.IsPrimary(),\n\t\tIdentities: FromIdentitiesModel(email.Identities, cfg),\n\t}\n\n\tif len(email.Identities) > 0 {\n\t\tidentity := FromIdentityModel(&email.Identities[0], cfg)\n\t\temailResponse.Identity = identity\n\t}\n\n\treturn emailResponse\n}\n\ntype EmailJWT struct {\n\tAddress    string `json:\"address\"`\n\tIsPrimary  bool   `json:\"is_primary\"`\n\tIsVerified bool   `json:\"is_verified\"`\n}\n\nfunc (e *EmailJWT) String() string {\n\tif e == nil {\n\t\treturn \"\"\n\t}\n\tjsonBytes, _ := json.Marshal(e)\n\treturn string(jsonBytes)\n}\n\nfunc EmailJWTFromEmailModel(email *models.Email) *EmailJWT {\n\tif email == nil {\n\t\treturn nil\n\t}\n\n\treturn &EmailJWT{\n\t\tAddress:    email.Address,\n\t\tIsPrimary:  email.IsPrimary(),\n\t\tIsVerified: email.Verified,\n\t}\n}\n"
  },
  {
    "path": "backend/dto/error_handler.go",
    "content": "package dto\n\nimport (\n\t\"fmt\"\n\t\"github.com/labstack/echo/v4\"\n\t\"net/http\"\n)\n\nfunc ToHttpError(err error) *echo.HTTPError {\n\tswitch e := err.(type) {\n\tcase *echo.HTTPError:\n\t\treturn &echo.HTTPError{\n\t\t\tCode:     e.Code,\n\t\t\tMessage:  fmt.Sprintf(\"%v\", e.Message),\n\t\t\tInternal: e.Internal,\n\t\t}\n\tdefault:\n\t\treturn &echo.HTTPError{\n\t\t\tCode:     http.StatusInternalServerError,\n\t\t\tMessage:  http.StatusText(http.StatusInternalServerError),\n\t\t\tInternal: err,\n\t\t}\n\t}\n}\n\ntype HTTPErrorHandlerConfig struct {\n\tDebug  bool\n\tLogger echo.Logger\n}\n\nfunc NewHTTPErrorHandler(config HTTPErrorHandlerConfig) func(err error, c echo.Context) {\n\treturn func(err error, c echo.Context) {\n\t\tif c.Response().Committed {\n\t\t\treturn\n\t\t}\n\n\t\therr := ToHttpError(err)\n\n\t\tcode := herr.Code\n\t\tmessage := echo.Map{\"code\": code, \"message\": herr.Message}\n\t\tif config.Debug {\n\t\t\tmessage = echo.Map{\"code\": code, \"message\": herr.Message, \"error\": err.Error()}\n\t\t}\n\n\t\t// Send response\n\t\tif c.Request().Method == http.MethodHead { // Issue https://github.com/labstack/echo/issues/608\n\t\t\terr = c.NoContent(code)\n\t\t} else {\n\t\t\terr = c.JSON(code, message)\n\t\t}\n\t\tif err != nil {\n\t\t\tconfig.Logger.Error(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "backend/dto/intern/WebauthnCredential.go",
    "content": "package intern\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\nfunc WebauthnCredentialToModel(credential *webauthn.Credential, userId uuid.UUID, backupEligible, backupState, mfaOnly bool, authenticatorMetadata mapper.AuthenticatorMetadata) *models.WebauthnCredential {\n\tnow := time.Now().UTC()\n\taaguid, _ := uuid.FromBytes(credential.Authenticator.AAGUID)\n\tcredentialID := base64.RawURLEncoding.EncodeToString(credential.ID)\n\n\tc := &models.WebauthnCredential{\n\t\tID:              credentialID,\n\t\tName:            authenticatorMetadata.GetNameForAaguid(aaguid),\n\t\tUserId:          userId,\n\t\tPublicKey:       base64.RawURLEncoding.EncodeToString(credential.PublicKey),\n\t\tAttestationType: credential.AttestationType,\n\t\tAAGUID:          aaguid,\n\t\tSignCount:       int(credential.Authenticator.SignCount),\n\t\tLastUsedAt:      &now,\n\t\tCreatedAt:       now,\n\t\tUpdatedAt:       now,\n\t\tBackupEligible:  backupEligible,\n\t\tBackupState:     backupState,\n\t\tMFAOnly:         mfaOnly,\n\t}\n\n\tfor _, name := range credential.Transport {\n\t\tif string(name) != \"\" {\n\t\t\tid, _ := uuid.NewV4()\n\t\t\tt := models.WebauthnCredentialTransport{\n\t\t\t\tID:                   id,\n\t\t\t\tName:                 string(name),\n\t\t\t\tWebauthnCredentialID: credentialID,\n\t\t\t}\n\t\t\tc.Transports = append(c.Transports, t)\n\t\t}\n\t}\n\n\treturn c\n}\n\nfunc WebauthnCredentialFromModel(credential *models.WebauthnCredential) *webauthn.Credential {\n\tcredId, _ := base64.RawURLEncoding.DecodeString(credential.ID)\n\tpKey, _ := base64.RawURLEncoding.DecodeString(credential.PublicKey)\n\ttransport := make([]protocol.AuthenticatorTransport, len(credential.Transports))\n\n\tfor i, t := range credential.Transports {\n\t\ttransport[i] = protocol.AuthenticatorTransport(t.Name)\n\t}\n\n\treturn &webauthn.Credential{\n\t\tID:              credId,\n\t\tPublicKey:       pKey,\n\t\tAttestationType: credential.AttestationType,\n\t\tAuthenticator: webauthn.Authenticator{\n\t\t\tAAGUID:    credential.AAGUID.Bytes(),\n\t\t\tSignCount: uint32(credential.SignCount),\n\t\t},\n\t\tTransport: transport,\n\t}\n}\n"
  },
  {
    "path": "backend/dto/intern/WebauthnSessionData.go",
    "content": "package intern\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\nfunc WebauthnSessionDataFromModel(data *models.WebauthnSessionData) *webauthn.SessionData {\n\tvar allowedCredentials [][]byte\n\tfor _, credential := range data.AllowedCredentials {\n\t\tcredentialId, err := base64.RawURLEncoding.DecodeString(credential.CredentialId)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tallowedCredentials = append(allowedCredentials, credentialId)\n\t}\n\tvar userId []byte = nil\n\tif !data.UserId.IsNil() {\n\t\tuserId = data.UserId.Bytes()\n\t}\n\treturn &webauthn.SessionData{\n\t\tChallenge:            data.Challenge,\n\t\tUserID:               userId,\n\t\tAllowedCredentialIDs: allowedCredentials,\n\t\tUserVerification:     protocol.UserVerificationRequirement(data.UserVerification),\n\t\tExpires:              data.ExpiresAt.Time,\n\t}\n}\n\nfunc WebauthnSessionDataToModel(data *webauthn.SessionData, operation models.Operation) *models.WebauthnSessionData {\n\tid, _ := uuid.NewV4()\n\tuserId, _ := uuid.FromBytes(data.UserID)\n\tnow := time.Now()\n\n\tvar allowedCredentials []models.WebauthnSessionDataAllowedCredential\n\tfor _, credentialID := range data.AllowedCredentialIDs {\n\t\taId, _ := uuid.NewV4()\n\t\tallowedCredential := models.WebauthnSessionDataAllowedCredential{\n\t\t\tID:                    aId,\n\t\t\tCredentialId:          base64.RawURLEncoding.EncodeToString(credentialID),\n\t\t\tWebauthnSessionDataID: id,\n\t\t\tCreatedAt:             now,\n\t\t\tUpdatedAt:             now,\n\t\t}\n\n\t\tallowedCredentials = append(allowedCredentials, allowedCredential)\n\t}\n\n\treturn &models.WebauthnSessionData{\n\t\tID:                 id,\n\t\tChallenge:          data.Challenge,\n\t\tUserId:             userId,\n\t\tUserVerification:   string(data.UserVerification),\n\t\tCreatedAt:          now,\n\t\tUpdatedAt:          now,\n\t\tOperation:          operation,\n\t\tAllowedCredentials: allowedCredentials,\n\t\tExpiresAt:          nulls.NewTime(data.Expires),\n\t}\n}\n"
  },
  {
    "path": "backend/dto/intern/WebauthnUser.go",
    "content": "package intern\n\nimport (\n\t\"errors\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\nfunc NewWebauthnUser(user models.User, credentials []models.WebauthnCredential) (*WebauthnUser, error) {\n\temail := user.Emails.GetPrimary()\n\tif email == nil {\n\t\treturn nil, errors.New(\"primary email unavailable\")\n\t}\n\n\treturn &WebauthnUser{\n\t\tUserId:              user.ID,\n\t\tEmail:               email.Address,\n\t\tWebauthnCredentials: credentials,\n\t}, nil\n}\n\ntype WebauthnUser struct {\n\tUserId              uuid.UUID\n\tEmail               string\n\tWebauthnCredentials []models.WebauthnCredential\n}\n\nfunc (u *WebauthnUser) WebAuthnID() []byte {\n\treturn u.UserId.Bytes()\n}\n\nfunc (u *WebauthnUser) WebAuthnName() string {\n\treturn u.Email\n}\n\nfunc (u *WebauthnUser) WebAuthnDisplayName() string {\n\treturn u.Email\n}\n\nfunc (u *WebauthnUser) WebAuthnIcon() string {\n\treturn \"\"\n}\n\nfunc (u *WebauthnUser) WebAuthnCredentials() []webauthn.Credential {\n\tvar credentials []webauthn.Credential\n\tfor _, credential := range u.WebauthnCredentials {\n\t\tcred := credential\n\t\tc := WebauthnCredentialFromModel(&cred)\n\t\tcredentials = append(credentials, *c)\n\t}\n\n\treturn credentials\n}\n"
  },
  {
    "path": "backend/dto/metadata.go",
    "content": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/tidwall/gjson\"\n\t\"strings\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\n// Metadata represents user metadata with public and unsafe fields\ntype Metadata struct {\n\tPublic json.RawMessage `json:\"public_metadata,omitempty\"`\n\tUnsafe json.RawMessage `json:\"unsafe_metadata,omitempty\"`\n}\n\n// NewMetadata creates a new Metadata DTO from a UserMetadata model\nfunc NewMetadata(metadata *models.UserMetadata) *Metadata {\n\tif metadata == nil {\n\t\treturn nil\n\t}\n\n\tresult := &Metadata{}\n\n\tif metadata.Public.Valid && metadata.Public.String != \"{}\" {\n\t\tresult.Public = json.RawMessage(metadata.Public.String)\n\t}\n\tif metadata.Unsafe.Valid && metadata.Unsafe.String != \"{}\" {\n\t\tresult.Unsafe = json.RawMessage(metadata.Unsafe.String)\n\t}\n\n\tif result.Public == nil && result.Unsafe == nil {\n\t\treturn nil\n\t}\n\n\treturn result\n}\n\n// MetadataJWT represents user metadata with public and unsafe fields. This metadata representation is used\n// for JWT template processing. Fields are private on purpose since the type provides dedicated methods with the same\n// name for accessing the data during template processing.\ntype MetadataJWT struct {\n\tpublic json.RawMessage\n\tunsafe json.RawMessage\n}\n\n// NewMetadataJWT creates a new MetadataJWT from public and unsafe metadata JSON raw messages. Primarily used in tests\n// to construct a MetadataJWT (due to private fields)\nfunc NewMetadataJWT(public, unsafe json.RawMessage) *MetadataJWT {\n\treturn &MetadataJWT{\n\t\tpublic: public,\n\t\tunsafe: unsafe,\n\t}\n}\n\n// MetadataJWTFromUserModel creates a new MetadataJWT DTO from a UserMetadata model\nfunc MetadataJWTFromUserModel(metadata *models.UserMetadata) *MetadataJWT {\n\tif metadata == nil {\n\t\treturn nil\n\t}\n\n\tresult := &MetadataJWT{}\n\n\tif metadata.Public.Valid && metadata.Public.String != \"{}\" {\n\t\tresult.public = json.RawMessage(metadata.Public.String)\n\t}\n\tif metadata.Unsafe.Valid && metadata.Unsafe.String != \"{}\" {\n\t\tresult.unsafe = json.RawMessage(metadata.Unsafe.String)\n\t}\n\n\tif result.public == nil && result.unsafe == nil {\n\t\treturn nil\n\t}\n\n\treturn result\n}\n\nfunc (m *MetadataJWT) Public(path ...string) string {\n\tif len(path) < 1 {\n\t\treturn gjson.GetBytes(m.public, \"@this\").String()\n\t}\n\n\treturn gjson.GetBytes(m.public, strings.Join(path, \".\")).String()\n}\n\nfunc (m *MetadataJWT) Unsafe(path ...string) string {\n\tif len(path) < 1 {\n\t\treturn gjson.GetBytes(m.unsafe, \"@this\").String()\n\t}\n\n\treturn gjson.GetBytes(m.unsafe, strings.Join(path, \".\")).String()\n}\n\nfunc (m *MetadataJWT) String() string {\n\tif m == nil {\n\t\treturn \"\"\n\t}\n\tjsonBytes, _ := json.Marshal(m)\n\treturn string(jsonBytes)\n}\n\nfunc (m *MetadataJWT) MarshalJSON() ([]byte, error) {\n\ts := struct {\n\t\tPublic json.RawMessage `json:\"public_metadata,omitempty\"`\n\t\tUnsafe json.RawMessage `json:\"unsafe_metadata,omitempty\"`\n\t}{\n\t\tPublic: m.public,\n\t\tUnsafe: m.unsafe,\n\t}\n\n\tjsonBytes, err := json.Marshal(s)\n\treturn jsonBytes, err\n}\n"
  },
  {
    "path": "backend/dto/passcode.go",
    "content": "package dto\n\nimport \"time\"\n\ntype PasscodeFinishRequest struct {\n\tId   string `json:\"id\" validate:\"required,uuid4\"`\n\tCode string `json:\"code\" validate:\"required\"`\n}\n\ntype PasscodeInitRequest struct {\n\tUserId  string  `json:\"user_id\" validate:\"required,uuid\"`\n\tEmailId *string `json:\"email_id\"`\n}\n\ntype PasscodeReturn struct {\n\tId        string    `json:\"id\"`\n\tTTL       int       `json:\"ttl\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n"
  },
  {
    "path": "backend/dto/profile.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype MFAConfig struct {\n\tAuthAppSetUp        bool `json:\"auth_app_set_up\"`\n\tTOTPEnabled         bool `json:\"totp_enabled\"`\n\tSecurityKeysEnabled bool `json:\"security_keys_enabled\"`\n}\n\ntype ProfileData struct {\n\t// Added for backward compatibility, since ProfileData is now returned\n\t// in the '/me' endpoint, which previously  returned the user ID as `id`.\n\tID           uuid.UUID                    `json:\"id\"`\n\tUserID       uuid.UUID                    `json:\"user_id\"`\n\tPasskeys     []WebauthnCredentialResponse `json:\"passkeys,omitempty\"`\n\tSecurityKeys []WebauthnCredentialResponse `json:\"security_keys,omitempty\"`\n\tMFAConfig    MFAConfig                    `json:\"mfa_config\"`\n\tEmails       []EmailResponse              `json:\"emails,omitempty\"`\n\tUsername     *Username                    `json:\"username,omitempty\"`\n\tCreatedAt    time.Time                    `json:\"created_at\"`\n\tUpdatedAt    time.Time                    `json:\"updated_at\"`\n\tMetadata     *Metadata                    `json:\"metadata,omitempty\"`\n\tIdentities   Identities                   `json:\"identities,omitempty\"`\n\tName         string                       `json:\"name,omitempty\"`\n\tGivenName    string                       `json:\"given_name,omitempty\"`\n\tFamilyName   string                       `json:\"family_name,omitempty\"`\n\tPicture      string                       `json:\"picture,omitempty\"`\n}\n\nfunc ProfileDataFromUserModel(user *models.User, cfg *config.Config) *ProfileData {\n\tvar webauthnCredentials, securityKeys []WebauthnCredentialResponse\n\tfor _, webauthnCredentialModel := range user.WebauthnCredentials {\n\t\twebauthnCredential := FromWebauthnCredentialModel(&webauthnCredentialModel)\n\t\tif cfg.MFA.SecurityKeys.Enabled && webauthnCredentialModel.MFAOnly {\n\t\t\tsecurityKeys = append(securityKeys, *webauthnCredential)\n\t\t} else if cfg.Passkey.Enabled && !webauthnCredentialModel.MFAOnly {\n\t\t\twebauthnCredentials = append(webauthnCredentials, *webauthnCredential)\n\t\t}\n\t}\n\n\tvar emails []EmailResponse\n\tfor _, emailModel := range user.Emails {\n\t\temail := FromEmailModel(&emailModel, cfg)\n\t\temails = append(emails, *email)\n\t}\n\n\tvar metadata *Metadata\n\tif user.Metadata != nil {\n\t\tmetadata = NewMetadata(user.Metadata)\n\t}\n\n\treturn &ProfileData{\n\t\tID:           user.ID,\n\t\tUserID:       user.ID,\n\t\tPasskeys:     webauthnCredentials,\n\t\tSecurityKeys: securityKeys,\n\t\tMFAConfig: MFAConfig{\n\t\t\tAuthAppSetUp:        user.OTPSecret != nil,\n\t\t\tTOTPEnabled:         cfg.MFA.Enabled && cfg.MFA.TOTP.Enabled,\n\t\t\tSecurityKeysEnabled: cfg.MFA.Enabled && cfg.MFA.SecurityKeys.Enabled,\n\t\t},\n\t\tEmails:     emails,\n\t\tUsername:   FromUsernameModel(user.Username),\n\t\tCreatedAt:  user.CreatedAt,\n\t\tUpdatedAt:  user.UpdatedAt,\n\t\tMetadata:   metadata,\n\t\tIdentities: FromIdentitiesModel(user.Identities, cfg),\n\t\tName:       user.Name.String,\n\t\tGivenName:  user.GivenName.String,\n\t\tFamilyName: user.FamilyName.String,\n\t\tPicture:    user.Picture.String,\n\t}\n}\n"
  },
  {
    "path": "backend/dto/session.go",
    "content": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/mileusna/useragent\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SessionData struct {\n\tID           uuid.UUID  `json:\"id\"`\n\tUserAgentRaw *string    `json:\"user_agent_raw,omitempty\"`\n\tUserAgent    *string    `json:\"user_agent,omitempty\"`\n\tIpAddress    *string    `json:\"ip_address,omitempty\"`\n\tCurrent      bool       `json:\"current\"`\n\tCreatedAt    time.Time  `json:\"created_at\"`\n\tExpiresAt    *time.Time `json:\"expires_at,omitempty\"`\n\tLastUsed     time.Time  `json:\"last_used\"`\n}\n\nfunc FromSessionModel(model models.Session, current bool) SessionData {\n\tsessionData := SessionData{\n\t\tID:        model.ID,\n\t\tCurrent:   current,\n\t\tCreatedAt: model.CreatedAt,\n\t\tExpiresAt: model.ExpiresAt,\n\t\tLastUsed:  model.LastUsed,\n\t}\n\n\tif model.UserAgent.Valid {\n\t\traw := model.UserAgent.String\n\t\tsessionData.UserAgentRaw = &raw\n\t\tua := useragent.Parse(model.UserAgent.String)\n\t\tparsed := fmt.Sprintf(\"%s (%s)\", ua.OS, ua.Name)\n\t\tsessionData.UserAgent = &parsed\n\t}\n\n\tif model.IpAddress.Valid {\n\t\ts := model.IpAddress.String\n\t\tsessionData.IpAddress = &s\n\t}\n\n\treturn sessionData\n}\n\ntype Claims struct {\n\tSubject      uuid.UUID              `json:\"subject\"`\n\tIssuedAt     *time.Time             `json:\"issued_at,omitempty\"`\n\tExpiration   time.Time              `json:\"expiration\"`\n\tAudience     []string               `json:\"audience,omitempty\"`\n\tIssuer       *string                `json:\"issuer,omitempty\"`\n\tEmail        *EmailJWT              `json:\"email,omitempty\"`\n\tUsername     *string                `json:\"username,omitempty\"`\n\tSessionID    uuid.UUID              `json:\"session_id\"`\n\tCustomClaims map[string]interface{} `json:\"-\"`\n}\n\n// Custom MarshalJSON to flatten CustomClaims into the top level\nfunc (c Claims) MarshalJSON() ([]byte, error) {\n\t// Create a map to hold the flattened structure\n\tflattened := make(map[string]interface{})\n\n\t// Marshal basic fields into the flattened map\n\tflattened[\"subject\"] = c.Subject\n\tflattened[\"expiration\"] = c.Expiration\n\tflattened[\"session_id\"] = c.SessionID\n\n\tif c.IssuedAt != nil {\n\t\tflattened[\"issued_at\"] = c.IssuedAt\n\t}\n\tif len(c.Audience) > 0 {\n\t\tflattened[\"audience\"] = c.Audience\n\t}\n\tif c.Issuer != nil {\n\t\tflattened[\"issuer\"] = c.Issuer\n\t}\n\tif c.Email != nil {\n\t\tflattened[\"email\"] = c.Email\n\t}\n\tif c.Username != nil {\n\t\tflattened[\"username\"] = c.Username\n\t}\n\n\t// Flatten CustomClaims into the top level\n\tfor key, value := range c.CustomClaims {\n\t\tflattened[key] = value\n\t}\n\n\treturn json.Marshal(flattened)\n}\n\nfunc GetClaimsFromToken(token jwt.Token) (*Claims, error) {\n\tclaims := &Claims{\n\t\tCustomClaims: make(map[string]interface{}),\n\t}\n\n\tif subject := token.Subject(); len(subject) > 0 {\n\t\ts, err := uuid.FromString(subject)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"'subject' is not a uuid: %w\", err)\n\t\t}\n\t\tclaims.Subject = s\n\t}\n\n\tif sessionID, valid := token.Get(\"session_id\"); valid {\n\t\ts, err := uuid.FromString(sessionID.(string))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"'session_id' is not a uuid: %w\", err)\n\t\t}\n\t\tclaims.SessionID = s\n\t}\n\n\tif issuedAt := token.IssuedAt(); !issuedAt.IsZero() {\n\t\tclaims.IssuedAt = &issuedAt\n\t}\n\n\tif audience := token.Audience(); len(audience) > 0 {\n\t\tclaims.Audience = audience\n\t}\n\n\tif issuer := token.Issuer(); len(issuer) > 0 {\n\t\tclaims.Issuer = &issuer\n\t}\n\n\tif v, ok := token.Get(\"email\"); ok {\n\t\tswitch t := v.(type) {\n\t\tcase *EmailJWT:\n\t\t\tclaims.Email = t\n\t\tcase EmailJWT:\n\t\t\tclaims.Email = &t\n\t\tcase map[string]interface{}:\n\t\t\tjsonData, err := json.Marshal(v)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to marshal 'email' claim: %w\", err)\n\t\t\t}\n\t\t\tvar ej EmailJWT\n\t\t\tif err := json.Unmarshal(jsonData, &ej); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to unmarshal 'email' claim: %w\", err)\n\t\t\t}\n\t\t\tclaims.Email = &ej\n\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unexpected 'email' claim type: %T\", v)\n\t\t}\n\t}\n\n\tif username, valid := token.Get(\"username\"); valid {\n\t\tif usernameStr, validStr := username.(string); validStr {\n\t\t\tclaims.Username = &usernameStr\n\t\t}\n\t}\n\n\tclaims.Expiration = token.Expiration()\n\n\thankoClaims := map[string]bool{\n\t\t\"email\":      true,\n\t\t\"username\":   true,\n\t\t\"session_id\": true,\n\t}\n\n\tfor key, value := range token.PrivateClaims() {\n\t\tif !hankoClaims[key] {\n\t\t\tclaims.CustomClaims[key] = value\n\t\t}\n\t}\n\n\treturn claims, nil\n}\n\ntype ValidateSessionResponse struct {\n\tIsValid bool    `json:\"is_valid\"`\n\tClaims  *Claims `json:\"claims,omitempty\"`\n\t// deprecated\n\tExpirationTime *time.Time `json:\"expiration_time,omitempty\"`\n\t// deprecated\n\tUserID *uuid.UUID `json:\"user_id,omitempty\"`\n}\n\ntype ValidateSessionRequest struct {\n\tSessionToken string `json:\"session_token\" validate:\"required\"`\n}\n"
  },
  {
    "path": "backend/dto/session_test.go",
    "content": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGetClaimsFromToken(t *testing.T) {\n\tsubject := uuid.Must(uuid.NewV4())\n\tsessionID := uuid.Must(uuid.NewV4())\n\tnow := time.Now()\n\texpiration := now.Add(1 * time.Hour)\n\n\ttests := []struct {\n\t\tname          string\n\t\ttoken         jwt.Token\n\t\texpected      *Claims\n\t\texpectedError string\n\t}{\n\t\t{\n\t\t\tname: \"valid token with all claims\",\n\t\t\ttoken: func() jwt.Token {\n\t\t\t\ttoken, _ := jwt.NewBuilder().\n\t\t\t\t\tSubject(subject.String()).\n\t\t\t\t\tIssuedAt(now).\n\t\t\t\t\tAudience([]string{\"test-audience\"}).\n\t\t\t\t\tIssuer(\"test-issuer\").\n\t\t\t\t\tExpiration(expiration).\n\t\t\t\t\tClaim(\"session_id\", sessionID.String()).\n\t\t\t\t\tClaim(\"email\", map[string]interface{}{\n\t\t\t\t\t\t\"address\":     \"test@example.com\",\n\t\t\t\t\t\t\"is_verified\": true,\n\t\t\t\t\t\t\"is_primary\":  true,\n\t\t\t\t\t}).\n\t\t\t\t\tClaim(\"username\", \"testuser\").\n\t\t\t\t\tClaim(\"custom\", \"value\").\n\t\t\t\t\tBuild()\n\t\t\t\treturn token\n\t\t\t}(),\n\t\t\texpected: &Claims{\n\t\t\t\tSubject:   subject,\n\t\t\t\tSessionID: sessionID,\n\t\t\t\tIssuedAt:  &now,\n\t\t\t\tAudience:  []string{\"test-audience\"},\n\t\t\t\tIssuer:    stringPtr(\"test-issuer\"),\n\t\t\t\tEmail: &EmailJWT{\n\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\tIsVerified: true,\n\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t},\n\t\t\t\tUsername:   stringPtr(\"testuser\"),\n\t\t\t\tExpiration: expiration,\n\t\t\t\tCustomClaims: map[string]interface{}{\n\t\t\t\t\t\"custom\": \"value\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"valid token with all claims (email as EmailJWT)\",\n\t\t\ttoken: func() jwt.Token {\n\t\t\t\ttoken, _ := jwt.NewBuilder().\n\t\t\t\t\tSubject(subject.String()).\n\t\t\t\t\tIssuedAt(now).\n\t\t\t\t\tAudience([]string{\"test-audience\"}).\n\t\t\t\t\tIssuer(\"test-issuer\").\n\t\t\t\t\tExpiration(expiration).\n\t\t\t\t\tClaim(\"session_id\", sessionID.String()).\n\t\t\t\t\tClaim(\"email\", EmailJWT{\n\t\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\t\tIsVerified: true,\n\t\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t\t}).\n\t\t\t\t\tClaim(\"username\", \"testuser\").\n\t\t\t\t\tClaim(\"custom\", \"value\").\n\t\t\t\t\tBuild()\n\t\t\t\treturn token\n\t\t\t}(),\n\t\t\texpected: &Claims{\n\t\t\t\tSubject:   subject,\n\t\t\t\tSessionID: sessionID,\n\t\t\t\tIssuedAt:  &now,\n\t\t\t\tAudience:  []string{\"test-audience\"},\n\t\t\t\tIssuer:    stringPtr(\"test-issuer\"),\n\t\t\t\tEmail: &EmailJWT{\n\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\tIsVerified: true,\n\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t},\n\t\t\t\tUsername:   stringPtr(\"testuser\"),\n\t\t\t\tExpiration: expiration,\n\t\t\t\tCustomClaims: map[string]interface{}{\n\t\t\t\t\t\"custom\": \"value\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tclaims, err := GetClaimsFromToken(tt.token)\n\t\t\tif tt.expectedError != \"\" {\n\t\t\t\tassert.Error(t, err)\n\t\t\t\tassert.Contains(t, err.Error(), tt.expectedError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotNil(t, claims)\n\n\t\t\t// Compare the claims\n\t\t\tif tt.expected != nil {\n\t\t\t\tassert.Equal(t, tt.expected.Subject, claims.Subject)\n\t\t\t\tassert.Equal(t, tt.expected.SessionID, claims.SessionID)\n\t\t\t\tassert.Equal(t, tt.expected.Audience, claims.Audience)\n\t\t\t\tassert.Equal(t, tt.expected.Issuer, claims.Issuer)\n\t\t\t\tassert.Equal(t, tt.expected.Username, claims.Username)\n\t\t\t\tassert.Equal(t, tt.expected.CustomClaims, claims.CustomClaims)\n\n\t\t\t\tif tt.expected.Email != nil {\n\t\t\t\t\tassert.Equal(t, tt.expected.Email.Address, claims.Email.Address)\n\t\t\t\t\tassert.Equal(t, tt.expected.Email.IsVerified, claims.Email.IsVerified)\n\t\t\t\t\tassert.Equal(t, tt.expected.Email.IsPrimary, claims.Email.IsPrimary)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClaims_MarshalJSON(t *testing.T) {\n\tsubject := uuid.Must(uuid.NewV4())\n\tsessionID := uuid.Must(uuid.NewV4())\n\tnow := time.Now().Truncate(time.Second)\n\texpiration := now.Add(1 * time.Hour)\n\tusername := \"testuser\"\n\tissuer := \"test-issuer\"\n\n\ttests := []struct {\n\t\tname     string\n\t\tclaims   Claims\n\t\texpected map[string]interface{}\n\t}{\n\t\t{\n\t\t\tname: \"all fields populated\",\n\t\t\tclaims: Claims{\n\t\t\t\tSubject:   subject,\n\t\t\t\tSessionID: sessionID,\n\t\t\t\tIssuedAt:  &now,\n\t\t\t\tAudience:  []string{\"test-audience\"},\n\t\t\t\tIssuer:    &issuer,\n\t\t\t\tEmail: &EmailJWT{\n\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\tIsVerified: true,\n\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t},\n\t\t\t\tUsername:   &username,\n\t\t\t\tExpiration: expiration,\n\t\t\t\tCustomClaims: map[string]interface{}{\n\t\t\t\t\t\"custom\": \"value\",\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\n\t\t\t\t\"subject\":    subject.String(),\n\t\t\t\t\"session_id\": sessionID.String(),\n\t\t\t\t\"issued_at\":  now,\n\t\t\t\t\"audience\":   []interface{}{\"test-audience\"},\n\t\t\t\t\"issuer\":     issuer,\n\t\t\t\t\"email\": map[string]interface{}{\n\t\t\t\t\t\"address\":     \"test@example.com\",\n\t\t\t\t\t\"is_verified\": true,\n\t\t\t\t\t\"is_primary\":  true,\n\t\t\t\t},\n\t\t\t\t\"username\":   username,\n\t\t\t\t\"expiration\": expiration,\n\t\t\t\t\"custom\":     \"value\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"minimal fields\",\n\t\t\tclaims: Claims{\n\t\t\t\tSubject:    subject,\n\t\t\t\tSessionID:  sessionID,\n\t\t\t\tExpiration: expiration,\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\n\t\t\t\t\"subject\":    subject.String(),\n\t\t\t\t\"session_id\": sessionID.String(),\n\t\t\t\t\"expiration\": expiration,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"with custom claims only\",\n\t\t\tclaims: Claims{\n\t\t\t\tSubject:    subject,\n\t\t\t\tSessionID:  sessionID,\n\t\t\t\tExpiration: expiration,\n\t\t\t\tCustomClaims: map[string]interface{}{\n\t\t\t\t\t\"custom1\": \"value1\",\n\t\t\t\t\t\"custom2\": \"value2\",\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\n\t\t\t\t\"subject\":    subject.String(),\n\t\t\t\t\"session_id\": sessionID.String(),\n\t\t\t\t\"expiration\": expiration,\n\t\t\t\t\"custom1\":    \"value1\",\n\t\t\t\t\"custom2\":    \"value2\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t// Marshal the claims to JSON\n\t\t\tjsonData, err := json.Marshal(tt.claims)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotEmpty(t, jsonData)\n\n\t\t\t// Unmarshal the JSON back to a map for comparison\n\t\t\tvar result map[string]interface{}\n\t\t\terr = json.Unmarshal(jsonData, &result)\n\t\t\tassert.NoError(t, err)\n\n\t\t\t// Compare the expected and actual results\n\t\t\tfor key, expectedValue := range tt.expected {\n\t\t\t\tactualValue := result[key]\n\t\t\t\tswitch v := expectedValue.(type) {\n\t\t\t\tcase time.Time:\n\t\t\t\t\t// For time values, compare the string representation after truncating to seconds\n\t\t\t\t\texpectedTime := v.Truncate(time.Second).UTC()\n\t\t\t\t\tactualTime, err := time.Parse(time.RFC3339, actualValue.(string))\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\tactualTime = actualTime.Truncate(time.Second).UTC()\n\t\t\t\t\tassert.Equal(t, expectedTime, actualTime, \"time mismatch for key: %s\", key)\n\t\t\t\tcase *time.Time:\n\t\t\t\t\t// For pointer to time values, compare the string representation after truncating to seconds\n\t\t\t\t\texpectedTime := v.Truncate(time.Second).UTC()\n\t\t\t\t\tactualTime, err := time.Parse(time.RFC3339, actualValue.(string))\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\tactualTime = actualTime.Truncate(time.Second).UTC()\n\t\t\t\t\tassert.Equal(t, expectedTime, actualTime, \"time mismatch for key: %s\", key)\n\t\t\t\tcase uuid.UUID:\n\t\t\t\t\t// For UUID values, compare the string representation\n\t\t\t\t\tassert.Equal(t, v.String(), actualValue, \"UUID mismatch for key: %s\", key)\n\t\t\t\tdefault:\n\t\t\t\t\tassert.Equal(t, expectedValue, actualValue, \"mismatch for key: %s\", key)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// Helper function to create a string pointer\nfunc stringPtr(s string) *string {\n\treturn &s\n}\n"
  },
  {
    "path": "backend/dto/thirdparty.go",
    "content": "package dto\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fatih/structs\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype ThirdPartyAuthCallback struct {\n\tAuthCode         string `query:\"code\"`\n\tState            string `query:\"state\" validate:\"required\"`\n\tError            string `query:\"error\"`\n\tErrorDescription string `query:\"error_description\"`\n}\n\nfunc (cb ThirdPartyAuthCallback) HasError() bool {\n\treturn cb.Error != \"\"\n}\n\ntype ThirdPartyAuthRequest struct {\n\tProvider   string `query:\"provider\" validate:\"required\"`\n\tRedirectTo string `query:\"redirect_to\" validate:\"required,url\"`\n}\n\ntype Identity struct {\n\tID         string    `json:\"id\"` // the user/subject ID at the provider, ProviderUserID from models.Identity\n\tProvider   string    `json:\"provider\"`\n\tIdentityID uuid.UUID `json:\"identity_id\"` // the internal id from models.Identity\n}\n\ntype Identities []Identity\n\nfunc FromIdentitiesModel(identities models.Identities, cfg *config.Config) Identities {\n\tvar result Identities\n\tfor _, i := range identities {\n\t\tidentity := FromIdentityModel(&i, cfg)\n\t\tresult = append(result, *identity)\n\t}\n\treturn result\n}\n\nfunc FromIdentityModel(identity *models.Identity, cfg *config.Config) *Identity {\n\tif identity == nil {\n\t\treturn nil\n\t}\n\n\treturn &Identity{\n\t\tID:         identity.ProviderUserID,\n\t\tProvider:   getProviderDisplayName(identity, cfg),\n\t\tIdentityID: identity.ID,\n\t}\n}\n\nfunc getProviderDisplayName(identity *models.Identity, cfg *config.Config) string {\n\tif identity.SamlIdentity != nil {\n\t\tfor _, ip := range cfg.Saml.IdentityProviders {\n\t\t\tif ip.Enabled && ip.Domain == identity.SamlIdentity.Domain {\n\t\t\t\treturn ip.Name\n\t\t\t}\n\t\t}\n\t} else if strings.HasPrefix(identity.ProviderID, \"custom_\") {\n\t\tproviderNameWithoutPrefix := strings.TrimPrefix(identity.ProviderID, \"custom_\")\n\t\treturn cfg.ThirdParty.CustomProviders[providerNameWithoutPrefix].DisplayName\n\t} else {\n\t\ts := structs.New(config.ThirdPartyProviders{})\n\t\tfor _, field := range s.Fields() {\n\t\t\tif strings.ToLower(field.Name()) == strings.ToLower(identity.ProviderID) {\n\t\t\t\treturn field.Name()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn strings.TrimSpace(identity.ProviderID)\n}\n"
  },
  {
    "path": "backend/dto/user.go",
    "content": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype CreateUserResponse struct {\n\tID      uuid.UUID `json:\"id\"` // deprecated\n\tUserID  uuid.UUID `json:\"user_id\"`\n\tEmailID uuid.UUID `json:\"email_id\"`\n}\n\ntype GetUserResponse struct {\n\tID                  uuid.UUID                   `json:\"id\"`\n\tEmail               *string                     `json:\"email,omitempty\"`\n\tUsername            *string                     `json:\"username,omitempty\"`\n\tWebauthnCredentials []models.WebauthnCredential `json:\"webauthn_credentials\"` // deprecated\n\tUpdatedAt           time.Time                   `json:\"updated_at\"`\n\tCreatedAt           time.Time                   `json:\"created_at\"`\n\tMetadata            *Metadata                   `json:\"metadata,omitempty\"`\n\tProfileData         `json:\",inline\"`\n}\n\ntype UserInfoResponse struct {\n\tID                    uuid.UUID `json:\"id\"`\n\tEmailID               uuid.UUID `json:\"email_id\"`\n\tVerified              bool      `json:\"verified\"`\n\tHasWebauthnCredential bool      `json:\"has_webauthn_credential\"`\n}\n\n// UserJWT represents an abstracted user model for session management\ntype UserJWT struct {\n\tUserID     string       `json:\"user_id\"`\n\tEmail      *EmailJWT    `json:\"email,omitempty\"`\n\tUsername   string       `json:\"username\"`\n\tMetadata   *MetadataJWT `json:\"metadata,omitempty\"`\n\tName       string       `json:\"name\"`\n\tFamilyName string       `json:\"family_name\"`\n\tGivenName  string       `json:\"given_name\"`\n\tPicture    string       `json:\"picture\"`\n}\n\nfunc (u *UserJWT) String() string {\n\tif u == nil {\n\t\treturn \"\"\n\t}\n\n\tjsonBytes, _ := json.Marshal(u)\n\treturn string(jsonBytes)\n}\n\nfunc UserJWTFromUserModel(userModel *models.User) UserJWT {\n\tuserJWT := UserJWT{\n\t\tUserID: userModel.ID.String(),\n\t}\n\n\tif primaryEmail := userModel.Emails.GetPrimary(); primaryEmail != nil {\n\t\tuserJWT.Email = EmailJWTFromEmailModel(primaryEmail)\n\t}\n\n\tif userModel.Username != nil {\n\t\tuserJWT.Username = userModel.Username.Username\n\t}\n\n\tif userModel.Metadata != nil {\n\t\tmetadataJWT := MetadataJWTFromUserModel(userModel.Metadata)\n\t\tif metadataJWT != nil {\n\t\t\tuserJWT.Metadata = metadataJWT\n\t\t}\n\t}\n\n\tif userModel.GivenName.Valid {\n\t\tuserJWT.GivenName = userModel.GivenName.String\n\t}\n\n\tif userModel.FamilyName.Valid {\n\t\tuserJWT.FamilyName = userModel.FamilyName.String\n\t}\n\n\tif userModel.Name.Valid {\n\t\tuserJWT.Name = userModel.Name.String\n\t}\n\n\tif userModel.Picture.Valid {\n\t\tuserJWT.Picture = userModel.Picture.String\n\t}\n\n\treturn userJWT\n}\n"
  },
  {
    "path": "backend/dto/username.go",
    "content": "package dto\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\ntype Username struct {\n\tID        uuid.UUID `json:\"id\"`\n\tUsername  string    `json:\"username\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\"`\n}\n\nfunc FromUsernameModel(u *models.Username) *Username {\n\tif u == nil {\n\t\treturn nil\n\t}\n\treturn &Username{\n\t\tID:        u.ID,\n\t\tUsername:  u.Username,\n\t\tCreatedAt: u.CreatedAt,\n\t\tUpdatedAt: u.UpdatedAt,\n\t}\n}\n"
  },
  {
    "path": "backend/dto/validator.go",
    "content": "package dto\n\nimport (\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"strings\"\n)\n\ntype CustomValidator struct {\n\tValidator *validator.Validate\n}\n\ntype ValidationErrors struct {\n\tErrors []string `json:\"errors\"`\n}\n\nfunc NewCustomValidator() *CustomValidator {\n\tv := validator.New()\n\n\t_ = v.RegisterValidation(\"hanko_event\", webhookEventValidator)\n\n\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\n\t\tif name == \"-\" {\n\t\t\treturn \"\"\n\t\t}\n\n\t\treturn name\n\t})\n\n\treturn &CustomValidator{Validator: v}\n}\n\nfunc (cv *CustomValidator) Validate(i interface{}) error {\n\tif err := cv.Validator.Struct(i); err != nil {\n\t\tvErrs := TransformValidationErrors(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, strings.Join(vErrs, \" and \"))\n\t}\n\n\treturn nil\n}\n\nfunc webhookEventValidator(fl validator.FieldLevel) bool {\n\treturn events.StringIsValidEvent(fl.Field().String())\n}\n\nfunc TransformValidationErrors(err error) []string {\n\tif fieldErrors, ok := err.(validator.ValidationErrors); ok {\n\t\tvErrs := make([]string, len(fieldErrors))\n\t\tfor i, err := range fieldErrors {\n\t\t\tswitch err.Tag() {\n\t\t\tcase \"required\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s is a required field\", err.Field())\n\t\t\tcase \"email\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s must be a valid email address\", err.Field())\n\t\t\tcase \"uuid\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s must be a valid uuid\", err.Field())\n\t\t\tcase \"uuid4\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s must be a valid uuid4\", err.Field())\n\t\t\tcase \"url\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s must be a valid URL\", err.Field())\n\t\t\tcase \"gte\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"length of %s must be greater or equal to %v\", err.Field(), err.Param())\n\t\t\tcase \"unique\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s entries are not unique\", err.Field())\n\t\t\tcase \"hanko_event\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s in %s is not a valid webhook event\", err.Value(), err.Field())\n\t\t\tcase \"ip\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s must be a valid ip address (v4 or v6)\", err.Field())\n\t\t\tcase \"required_if\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s is required if %v\", err.Field(), err.Param())\n\t\t\tcase \"min\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"length of %s must be greater or equal to %v\", err.Field(), err.Param())\n\t\t\tcase \"excluded_if\":\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"%s must not be set when %s\", err.Field(), err.Param())\n\t\t\tdefault:\n\t\t\t\tvErrs[i] = fmt.Sprintf(\"something wrong on %s; %s\", err.Field(), err.Tag())\n\t\t\t}\n\t\t}\n\t\treturn vErrs\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "backend/dto/webauthn.go",
    "content": "package dto\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\ntype WebauthnCredentialUpdateRequest struct {\n\tName *string `json:\"name\"`\n}\n\ntype WebauthnCredentialResponse struct {\n\tID              string     `json:\"id\"`\n\tName            *string    `json:\"name,omitempty\"`\n\tPublicKey       string     `json:\"public_key\"`\n\tAttestationType string     `json:\"attestation_type\"`\n\tAAGUID          uuid.UUID  `json:\"aaguid\"`\n\tLastUsedAt      *time.Time `json:\"last_used_at,omitempty\"`\n\tCreatedAt       time.Time  `json:\"created_at\"`\n\tTransports      []string   `json:\"transports\"`\n\tBackupEligible  bool       `json:\"backup_eligible\"`\n\tBackupState     bool       `json:\"backup_state\"`\n\tMFAOnly         bool       `json:\"mfa_only\"`\n}\n\n// FromWebauthnCredentialModel Converts the DB model to a DTO object\nfunc FromWebauthnCredentialModel(c *models.WebauthnCredential) *WebauthnCredentialResponse {\n\treturn &WebauthnCredentialResponse{\n\t\tID:              c.ID,\n\t\tName:            c.Name,\n\t\tPublicKey:       c.PublicKey,\n\t\tAttestationType: c.AttestationType,\n\t\tAAGUID:          c.AAGUID,\n\t\tLastUsedAt:      c.LastUsedAt,\n\t\tCreatedAt:       c.CreatedAt,\n\t\tTransports:      c.Transports.GetNames(),\n\t\tBackupEligible:  c.BackupEligible,\n\t\tBackupState:     c.BackupState,\n\t\tMFAOnly:         c.MFAOnly,\n\t}\n}\n"
  },
  {
    "path": "backend/dto/webhook/email.go",
    "content": "package webhook\n\ntype EmailSend struct {\n\tSubject          string `json:\"subject\"`        // subject\n\tBodyPlain        string `json:\"body_plain\"`     // used for string templates\n\tBody             string `json:\"body,omitempty\"` // used for HTML templates\n\tToEmailAddress   string `json:\"to_email_address\"`\n\tDeliveredByHanko bool   `json:\"delivered_by_hanko\"`\n\tAcceptLanguage   string `json:\"accept_language\"` // Deprecated. Accept-Language header from HTTP request\n\tLanguage         string `json:\"language\"`        // X-Language header from HTTP request\n\tType             string `json:\"type\"`            // type of the email\n\n\tData interface{} `json:\"data\"`\n}\n\ntype PasscodeData struct {\n\tServiceName string `json:\"service_name,omitempty\"`\n\tOtpCode     string `json:\"otp_code,omitempty\"`\n\tTTL         int    `json:\"ttl,omitempty\"`\n\tValidUntil  int64  `json:\"valid_until,omitempty\"` // UnixTimestamp\n}\n\ntype SecurityNotificationData struct {\n\tTemplate            string `json:\"template\"`\n\tServiceName         string `json:\"service_name,omitempty\"`\n\tNewEmailAddress     string `json:\"new_email_address,omitempty\"`\n\tOldEmailAddress     string `json:\"old_email_address,omitempty\"`\n\tDeletedEmailAddress string `json:\"deleted_email_address,omitempty\"`\n}\n"
  },
  {
    "path": "backend/ee/saml/config/saml.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobwas/glob\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype Saml struct {\n\t// `enabled` determines whether the SAML API endpoints are available.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n\t// `endpoint` is URL at which the SAML endpoints like metadata, callback, etc. are available\n\t// (e.g. `{YOUR_BACKEND_INSTANCE}/api`).\n\t//\n\t// Will be provided as metadata for IdP.\n\tEndpoint string `yaml:\"endpoint_url\" json:\"endpoint_url,omitempty\" koanf:\"endpoint_url\"`\n\t// `audience_uri` determines the intended recipient or audience for the SAML Assertion.\n\tAudienceUri string `yaml:\"audience_uri\" json:\"audience_uri,omitempty\" koanf:\"audience_uri\"`\n\t// `default_redirect_url` is the URL to redirect to in case of errors or when no `allowed_redirect_url` is provided.\n\tDefaultRedirectUrl string `yaml:\"default_redirect_url\" json:\"default_redirect_url,omitempty\" koanf:\"default_redirect_url\"`\n\t// `allowed_redirect_urls` is a list of URLs the backend is allowed to redirect to after third party sign-in was\n\t// successful.\n\t//\n\t// Supports wildcard matching through globbing. e.g. `https://*.example.com` will allow `https://foo.example.com`\n\t// and `https://bar.example.com` to be accepted.\n\t//\n\t// Globbing is also supported for paths, e.g. `https://foo.example.com/*` will match `https://foo.example.com/page1`\n\t// and `https://foo.example.com/page2`.\n\t//\n\t// A double asterisk (`**`) acts as a \"super\"-wildcard/match-all.\n\t//\n\t// See [here](https://pkg.go.dev/github.com/gobwas/glob#Compile) for more on globbinh.\n\tAllowedRedirectURLS   []string             `yaml:\"allowed_redirect_urls\" json:\"allowed_redirect_urls,omitempty\" koanf:\"allowed_redirect_urls\" split_words:\"true\"`\n\tAllowedRedirectURLMap map[string]glob.Glob `jsonschema:\"-\"`\n\n\t// `options` allows setting optional features for service provider operations.\n\tOptions Options `yaml:\"options\" json:\"options,omitempty\" koanf:\"options\" jsonschema:\"title=options\"`\n\n\t// `identity_providers` is a list of SAML identity providers.\n\tIdentityProviders []IdentityProvider `yaml:\"identity_providers\" json:\"identity_providers,omitempty\" koanf:\"identity_providers\"`\n}\n\nfunc (s Saml) GetProviderByDomain(domain string) *IdentityProvider {\n\tfor _, ip := range s.IdentityProviders {\n\t\tif ip.Domain == domain {\n\t\t\treturn &ip\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype Options struct {\n\t// `sign_authn_requests` determines whether initial requests should be signed.\n\tSignAuthnRequests bool `yaml:\"sign_authn_requests\" json:\"sign_authn_requests,omitempty\" koanf:\"sign_authn_requests\" jsonschema:\"default=true\"`\n\t// `force_login` forces the IdP to always show a login (even if there is an active session with the IdP).\n\tForceLogin bool `yaml:\"force_login\" json:\"force_login,omitempty\" koanf:\"force_login\" jsonschema:\"default=false\"`\n\t// `validate_encryption_cert` determines whether the certificate used for the encryption of the IdP responses should\n\t// be checked for validity.\n\tValidateEncryptionCertificate bool `yaml:\"validate_encryption_cert\" json:\"validate_encryption_cert,omitempty\" koanf:\"validate_encryption_cert\" jsonschema:\"default=true\"`\n\t// `skip_signature_validation` determines whether the validity check of an IdP response's signature\n\t// should be skipped.\n\tSkipSignatureValidation bool `yaml:\"skip_signature_validation\" json:\"skip_signature_validation,omitempty\" koanf:\"skip_signature_validation\" jsonschema:\"default=false\"`\n\t// `allow_missing_attributes` determines whether missing attributes are allowed (e.g. the IdP specifies a phone\n\t// attribute in the metadata but does not send it with a SAML Assertion Response).\n\tAllowMissingAttributes bool `yaml:\"allow_missing_attributes\" json:\"allow_missing_attributes,omitempty\" koanf:\"allow_missing_attributes\" jsonschema:\"default=false\"`\n}\n\ntype IdentityProvider struct {\n\t// `enabled` activates or deactivates the identity provider.\n\tEnabled bool `yaml:\"enabled\" json:\"enabled,omitempty\" koanf:\"enabled\" jsonschema:\"default=false\"`\n\t// `name` is the name given for the identity provider.\n\tName string `yaml:\"name\" json:\"name,omitempty\" koanf:\"name\"`\n\t// At login the domain will be extracted from the users email address and then used to identify the idp to use.\n\t// This tag defines for which domain the idp is used.\n\tDomain string `yaml:\"domain\" json:\"domain,omitempty\" koanf:\"domain\"`\n\t// `metadata_url` is the URL the API can retrieve IdP metadata from.\n\tMetadataUrl string `yaml:\"metadata_url\" json:\"metadata_url,omitempty\" koanf:\"metadata_url\"`\n\t// `skip_email_verification` determines whether the check if the `email_verified` attribute in the IdP response\n\t// will be skipped.\n\tSkipEmailVerification bool `yaml:\"skip_email_verification\" json:\"skip_email_verification,omitempty\" koanf:\"skip_email_verification\"`\n\t// `attribute_map` is a map of attributes used to map attributes in IdP response to custom attributes at\n\t// Hanko.\n\tAttributeMap AttributeMap `yaml:\"attribute_map\" json:\"attribute_map,omitempty\" koanf:\"attribute_map\" jsonschema:\"title=attribute_map\"`\n}\n\ntype AttributeMap struct {\n\tName              string `yaml:\"name\" json:\"name,omitempty\" koanf:\"name\" jsonschema:\"default=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\"`\n\tFamilyName        string `yaml:\"family_name\" json:\"family_name,omitempty\" koanf:\"family_name\" jsonschema:\"default=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname\"`\n\tGivenName         string `yaml:\"given_name\" json:\"given_name,omitempty\" koanf:\"given_name\" jsonschema:\"default=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname\"`\n\tMiddleName        string `yaml:\"middle_name\" json:\"middle_name,omitempty\" koanf:\"middle_name\"`\n\tNickName          string `yaml:\"nickname\" json:\"nickname,omitempty\" koanf:\"nickname\"`\n\tPreferredUsername string `yaml:\"preferred_username\" json:\"preferred_username,omitempty\" koanf:\"preferred_username\"`\n\tProfile           string `yaml:\"profile\" json:\"profile,omitempty\" koanf:\"profile\"`\n\tPicture           string `yaml:\"picture\" json:\"picture,omitempty\" koanf:\"picture\"`\n\tWebsite           string `yaml:\"website\" json:\"website,omitempty\" koanf:\"website\"`\n\tGender            string `yaml:\"gender\" json:\"gender,omitempty\" koanf:\"gender\"`\n\tBirthdate         string `yaml:\"birthdate\" json:\"birthdate,omitempty\" koanf:\"birthdate\"`\n\tZoneInfo          string `yaml:\"zone_info\" json:\"zone_info,omitempty\" koanf:\"zone_info\"`\n\tLocale            string `yaml:\"locale\" json:\"locale,omitempty\" koanf:\"locale\"`\n\tUpdatedAt         string `yaml:\"updated_at\" json:\"updated_at,omitempty\" koanf:\"updated_at\"`\n\tEmail             string `yaml:\"email\" json:\"email,omitempty\" koanf:\"email\" jsonschema:\"default=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"`\n\tEmailVerified     string `yaml:\"email_verified\" json:\"email_verified,omitempty\" koanf:\"email_verified\"`\n\tPhone             string `yaml:\"phone\" json:\"phone,omitempty\" koanf:\"phone\"`\n\tPhoneVerified     string `yaml:\"phone_verified\" json:\"phone_verified,omitempty\" koanf:\"phone_verified\"`\n}\n\nfunc (s *Saml) PostProcess() error {\n\ts.Endpoint = strings.TrimSuffix(s.Endpoint, \"/\")\n\n\ts.AllowedRedirectURLMap = make(map[string]glob.Glob)\n\turls := append(s.AllowedRedirectURLS, s.DefaultRedirectUrl)\n\tfor _, redirectUrl := range urls {\n\t\tglobbedUrl, err := glob.Compile(redirectUrl, '.', '/')\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed compile allowed redirect url glob: %w\", err)\n\t\t}\n\t\ts.AllowedRedirectURLMap[redirectUrl] = globbedUrl\n\t}\n\n\treturn nil\n}\n\nfunc (s *Saml) Validate() error {\n\tif s.Enabled {\n\t\tvalidationErrors := s.ValidateEmpty()\n\t\tif validationErrors != nil {\n\t\t\treturn validationErrors\n\t\t}\n\n\t\tvalidationErrors = s.ValidateUrls()\n\t\tif validationErrors != nil {\n\t\t\treturn validationErrors\n\t\t}\n\n\t\tif len(s.IdentityProviders) == 0 {\n\t\t\treturn errors.New(\"at least one SAML provider is needed\")\n\t\t}\n\n\t\tconfiguredDomains := make(map[string]int)\n\t\tfor _, provider := range s.IdentityProviders {\n\t\t\tif provider.Enabled {\n\t\t\t\tvalidationErrors = provider.Validate()\n\t\t\t\tif validationErrors != nil {\n\t\t\t\t\treturn validationErrors\n\t\t\t\t}\n\n\t\t\t\tconfiguredDomains[provider.Domain] += 1\n\t\t\t}\n\t\t}\n\n\t\tfor configuredDomain, configuredDomainCount := range configuredDomains {\n\t\t\tif configuredDomainCount > 1 {\n\t\t\t\treturn fmt.Errorf(\"provider domains must be unique, found domain %s configured %d times\", configuredDomain, configuredDomainCount)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Saml) ValidateEmpty() error {\n\tif strings.TrimSpace(s.Endpoint) == \"\" {\n\t\treturn errors.New(\"endpoint_url must be set\")\n\t}\n\n\tif strings.TrimSpace(s.AudienceUri) == \"\" {\n\t\treturn errors.New(\"audience_uri must be set\")\n\t}\n\n\treturn nil\n}\n\nconst (\n\tinvalidUrlFormat = \"'%s' is not a valid url\"\n)\n\nfunc (s *Saml) ValidateUrls() error {\n\t_, err := url.Parse(s.Endpoint)\n\tif err != nil {\n\t\treturn fmt.Errorf(invalidUrlFormat, s.Endpoint)\n\t}\n\n\t_, err = url.Parse(s.DefaultRedirectUrl)\n\tif err != nil {\n\t\treturn fmt.Errorf(invalidUrlFormat, s.DefaultRedirectUrl)\n\t}\n\n\tfor _, redirectUrl := range s.AllowedRedirectURLS {\n\t\t_, err = url.Parse(s.DefaultRedirectUrl)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(invalidUrlFormat, redirectUrl)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (idp *IdentityProvider) Validate() error {\n\tif strings.TrimSpace(idp.Domain) == \"\" {\n\t\treturn errors.New(\"domain must be set\")\n\t}\n\n\tif strings.TrimSpace(idp.Name) == \"\" {\n\t\treturn errors.New(\"name must be set\")\n\t}\n\n\t_, err := url.Parse(fmt.Sprintf(\"http://%s\", idp.Domain))\n\tif err != nil {\n\t\treturn fmt.Errorf(invalidUrlFormat, idp.Domain)\n\t}\n\n\tif strings.TrimSpace(idp.MetadataUrl) == \"\" {\n\t\treturn errors.New(\"identity provider metadata url must be set\")\n\t}\n\n\t_, err = url.Parse(idp.MetadataUrl)\n\tif err != nil {\n\t\treturn fmt.Errorf(invalidUrlFormat, idp.MetadataUrl)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/ee/saml/config/saml_test.go",
    "content": "package config\n\nimport (\n\t\"github.com/gobwas/glob\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestSamlConfig_PostProcess(t *testing.T) {\n\tredirectUrls := []string{\"http://allowed-redirect/lorem\"}\n\n\tcfg := &Saml{\n\t\tEndpoint:            \"http://lorem.ipsum/\",\n\t\tDefaultRedirectUrl:  \"http://localhost:8000\",\n\t\tAllowedRedirectURLS: redirectUrls,\n\t}\n\tglobbedUrl, err := glob.Compile(cfg.DefaultRedirectUrl, '.', '/')\n\tassert.NoError(t, err)\n\n\terr = cfg.PostProcess()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, cfg.AllowedRedirectURLMap)\n\tassert.Len(t, cfg.AllowedRedirectURLMap, 2)\n\tassert.Equal(t, globbedUrl, cfg.AllowedRedirectURLMap[cfg.DefaultRedirectUrl])\n}\n\nfunc TestSamlConfig_PostProcessWithBrokenGlob(t *testing.T) {\n\tredirectUrls := []string{\"http://allowed-redirect/lorem\"}\n\n\tcfg := &Saml{\n\t\tEndpoint:            \"http://lorem.ipsum/\",\n\t\tDefaultRedirectUrl:  \"[\",\n\t\tAllowedRedirectURLS: redirectUrls,\n\t}\n\n\terr := cfg.PostProcess()\n\tassert.NotNil(t, err)\n\tassert.ErrorContains(t, err, \"failed compile allowed redirect url glob\")\n}\n\nfunc TestSamlConfig_PostProcessEndpointTrimme(t *testing.T) {\n\tredirectUrls := []string{\"http://allowed-redirect/lorem\"}\n\n\tcfg := &Saml{\n\t\tEndpoint:            \"http://lorem.ipsum/\",\n\t\tDefaultRedirectUrl:  \"http://localhost:8000\",\n\t\tAllowedRedirectURLS: redirectUrls,\n\t}\n\n\terr := cfg.PostProcess()\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"http://lorem.ipsum\", cfg.Endpoint)\n}\n\nfunc TestSamlConfig_ValidateWithDisabledSaml(t *testing.T) {\n\tcfg := &Saml{\n\t\tEnabled: false,\n\t}\n\n\terr := cfg.Validate()\n\tassert.NoError(t, err)\n}\n\nfunc TestSamlConfig_ValidateWithEnabledSaml(t *testing.T) {\n\tcfg := &Saml{\n\t\tEnabled:               true,\n\t\tEndpoint:              \"http://lorem.ipsum/\",\n\t\tAudienceUri:           \"urn:Hanko\",\n\t\tDefaultRedirectUrl:    \"http://lorem.ipsum/saml\",\n\t\tAllowedRedirectURLS:   []string{},\n\t\tAllowedRedirectURLMap: nil,\n\t\tIdentityProviders: []IdentityProvider{{\n\t\t\tEnabled:     true,\n\t\t\tName:        \"Test Provider\",\n\t\t\tDomain:      \"lorem.ipsum\",\n\t\t\tMetadataUrl: \"http://provider.lorem.ipsum/metadata\",\n\t\t}},\n\t}\n\n\terr := cfg.Validate()\n\tassert.NoError(t, err)\n}\n\nfunc TestSamlConfig_ValidateEmpty(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint:    \"lorem\",\n\t\tAudienceUri: \"ipsum\",\n\t}\n\n\terr := cfg.ValidateEmpty()\n\tassert.NoError(t, err)\n}\n\nfunc TestSamlConfig_ValidateEmptyErrorWithEmptyEndpoint(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint: \"\",\n\t}\n\n\terr := cfg.ValidateEmpty()\n\tassert.Equal(t, \"endpoint_url must be set\", err.Error())\n}\n\nfunc TestSamlConfig_ValidateEmptyErrorWithSpaceEndpoint(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint: \"  \",\n\t}\n\n\terr := cfg.ValidateEmpty()\n\tassert.Equal(t, \"endpoint_url must be set\", err.Error())\n}\n\nfunc TestSamlConfig_ValidateEmptyErrorWithEmptyAudienceUri(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint:    \"http://lorem.ipsum\",\n\t\tAudienceUri: \"\",\n\t}\n\n\terr := cfg.ValidateEmpty()\n\tassert.Equal(t, \"audience_uri must be set\", err.Error())\n}\n\nfunc TestSamlConfig_ValidateEmptyErrorWithSpaceAudienceUri(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint:    \"http://lorem.ipsum\",\n\t\tAudienceUri: \"  \",\n\t}\n\n\terr := cfg.ValidateEmpty()\n\tassert.Equal(t, \"audience_uri must be set\", err.Error())\n}\n\nfunc TestSamlConfig_ValidateUrls(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint:            \"http://lorem.ipsum\",\n\t\tDefaultRedirectUrl:  \"http://lorem.ipsum/redirect\",\n\t\tAllowedRedirectURLS: []string{\"http://lorem.ipsum/allowed\"},\n\t}\n\n\terr := cfg.ValidateUrls()\n\tassert.NoError(t, err)\n}\n\nfunc TestSamlConfig_ValidateUrlsWithWrongEndpointUrl(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint: \"http://lorem:8000.de/ipsum\",\n\t}\n\n\terr := cfg.ValidateUrls()\n\tassert.Errorf(t, err, invalidUrlFormat, cfg.Endpoint)\n}\n\nfunc TestSamlConfig_ValidateUrlsWithWrongDefaultRedirecttUrl(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint:           \"http://lorem.ipsum\",\n\t\tDefaultRedirectUrl: \"http://lorem:8000.de/ipsum\",\n\t}\n\n\terr := cfg.ValidateUrls()\n\tassert.Errorf(t, err, invalidUrlFormat, cfg.DefaultRedirectUrl)\n}\n\nfunc TestSamlConfig_ValidateUrlsWithWrongAllowedRedirectUrl(t *testing.T) {\n\tcfg := &Saml{\n\t\tEndpoint:            \"http://lorem.ipsum\",\n\t\tDefaultRedirectUrl:  \"http://lorem:8000.de/ipsum\",\n\t\tAllowedRedirectURLS: []string{\"s:/\"},\n\t}\n\n\terr := cfg.ValidateUrls()\n\tassert.Errorf(t, err, invalidUrlFormat, cfg.AllowedRedirectURLS[0])\n}\n\nfunc TestSamlConfig_ValidateProvider(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tEnabled:     true,\n\t\tName:        \"Lorem\",\n\t\tDomain:      \"lorem.ipsum\",\n\t\tMetadataUrl: \"http://provider.lorem.ipsum/metadata\",\n\t}\n\n\terr := cfg.Validate()\n\tassert.NoError(t, err)\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithEmnptyDomain(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain: \"\",\n\t}\n\n\terr := cfg.Validate()\n\tassert.ErrorContains(t, err, \"domain must be set\")\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithSpaceDomain(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain: \"  \",\n\t}\n\n\terr := cfg.Validate()\n\tassert.ErrorContains(t, err, \"domain must be set\")\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithEmptyName(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain: \"lorem.ipsum\",\n\t\tName:   \"\",\n\t}\n\n\terr := cfg.Validate()\n\tassert.ErrorContains(t, err, \"name must be set\")\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithSpaceName(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain: \"lorem.ipsum\",\n\t\tName:   \"  \",\n\t}\n\n\terr := cfg.Validate()\n\tassert.ErrorContains(t, err, \"name must be set\")\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithInvalidDomain(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain: \"lorem..ipsum\",\n\t\tName:   \"Test\",\n\t}\n\n\terr := cfg.Validate()\n\tassert.Errorf(t, err, invalidUrlFormat, cfg.Domain)\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithEmptyMetadataUrl(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain:      \"lorem.ipsum\",\n\t\tName:        \"Test\",\n\t\tMetadataUrl: \"\",\n\t}\n\n\terr := cfg.Validate()\n\tassert.ErrorContains(t, err, \"identity provider metadata url must be set\")\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithSpaceMetadataUrl(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain:      \"lorem.ipsum\",\n\t\tName:        \"Test\",\n\t\tMetadataUrl: \"  \",\n\t}\n\n\terr := cfg.Validate()\n\tassert.ErrorContains(t, err, \"identity provider metadata url must be set\")\n}\n\nfunc TestSamlConfig_ValidateProviderErrorWithInvalidMetadataUrl(t *testing.T) {\n\tcfg := &IdentityProvider{\n\t\tDomain:      \"lorem.ipsum\",\n\t\tName:        \"Test\",\n\t\tMetadataUrl: \"http://lorem:8000.de/ipsum\",\n\t}\n\n\terr := cfg.Validate()\n\tassert.Errorf(t, err, invalidUrlFormat, cfg.MetadataUrl)\n}\n"
  },
  {
    "path": "backend/ee/saml/dto/saml.go",
    "content": "package dto\n\ntype SamlRequest struct {\n\tDomain string `query:\"domain\" validate:\"required,fqdn\"`\n}\n\ntype SamlMetadataRequest struct {\n\tSamlRequest\n\tCertOnly bool `query:\"cert_only\" validate:\"boolean\"`\n}\n\ntype SamlAuthRequest struct {\n\tSamlRequest\n\tRedirectTo string `query:\"redirect_to\" validate:\"required,url\"`\n}\n"
  },
  {
    "path": "backend/ee/saml/handler.go",
    "content": "package saml\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\tsaml2 \"github.com/russellhaering/gosaml2\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml/provider\"\n\tsamlUtils \"github.com/teamhanko/hanko/backend/v2/ee/saml/utils\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n)\n\ntype Handler struct {\n\tauditLogger    auditlog.Logger\n\tsessionManager session.Manager\n\tsamlService    Service\n}\n\nfunc NewSamlHandler(sessionManager session.Manager, auditLogger auditlog.Logger, samlService Service) *Handler {\n\treturn &Handler{\n\t\tauditLogger:    auditLogger,\n\t\tsessionManager: sessionManager,\n\t\tsamlService:    samlService,\n\t}\n}\n\nfunc (handler *Handler) Metadata(c echo.Context) error {\n\tvar request dto.SamlMetadataRequest\n\terr := c.Bind(&request)\n\tif err != nil {\n\t\treturn c.JSON(http.StatusBadRequest, thirdparty.ErrorInvalidRequest(\"domain is missing\"))\n\t}\n\n\tfoundProvider, err := handler.samlService.GetProviderByDomain(request.Domain)\n\tif err != nil {\n\t\treturn c.NoContent(http.StatusNotFound)\n\t}\n\n\tif request.CertOnly {\n\t\tcert, err := handler.samlService.Persister().GetSamlCertificatePersister().GetFirst()\n\t\tif err != nil {\n\t\t\treturn c.JSON(http.StatusInternalServerError, thirdparty.ErrorServer(\"unable to provide metadata\").WithCause(err))\n\t\t}\n\n\t\tif cert == nil {\n\t\t\treturn c.NoContent(http.StatusNotFound)\n\t\t}\n\n\t\tc.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf(\"attachment; filename=%s-service-provider.pem\", handler.samlService.Config().Service.Name))\n\t\treturn c.Blob(http.StatusOK, echo.MIMEOctetStream, []byte(cert.CertData))\n\t}\n\n\txmlMetadata, err := foundProvider.ProvideMetadataAsXml()\n\tif err != nil {\n\t\treturn c.JSON(http.StatusInternalServerError, thirdparty.ErrorServer(\"unable to provide metadata\").WithCause(err))\n\t}\n\n\tc.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf(\"attachment; filename=%s-metadata.xml\", handler.samlService.Config().Service.Name))\n\treturn c.Blob(http.StatusOK, echo.MIMEOctetStream, xmlMetadata)\n}\n\nfunc (handler *Handler) Auth(c echo.Context) error {\n\terrorRedirectTo := c.Request().Header.Get(\"Referer\")\n\tif errorRedirectTo == \"\" {\n\t\terrorRedirectTo = handler.samlService.Config().Saml.DefaultRedirectUrl\n\t}\n\n\tvar request dto.SamlAuthRequest\n\terr := c.Bind(&request)\n\tif err != nil {\n\t\treturn handler.redirectError(c, thirdparty.ErrorInvalidRequest(err.Error()).WithCause(err), errorRedirectTo)\n\t}\n\n\terr = c.Validate(request)\n\tif err != nil {\n\t\treturn handler.redirectError(c, thirdparty.ErrorInvalidRequest(err.Error()).WithCause(err), errorRedirectTo)\n\t}\n\n\tfoundProvider, err := handler.samlService.GetProviderByDomain(request.Domain)\n\tif err != nil {\n\t\treturn handler.redirectError(c, thirdparty.ErrorInvalidRequest(err.Error()).WithCause(err), errorRedirectTo)\n\t}\n\n\tredirectUrl, err := handler.samlService.GetAuthUrl(foundProvider, request.RedirectTo, false)\n\tif err != nil {\n\t\treturn handler.redirectError(c, thirdparty.ErrorServer(\"could not generate auth url\").WithCause(err), errorRedirectTo)\n\t}\n\n\treturn c.Redirect(http.StatusTemporaryRedirect, redirectUrl)\n}\n\nfunc (handler *Handler) callbackPostIdPInitiated(c echo.Context, samlResponse string) error {\n\t// ignore URL parse error because config validation already ensures it is a parseable URL\n\tredirectTo, _ := url.Parse(handler.samlService.Config().Saml.DefaultRedirectUrl)\n\n\t// We need to already parse the response to be able to extract information (a response's ID, Issuer, InResponseTo\n\t// nodes/values) to ensure protection against replaying IDP initiated responses as well as using service provider\n\t// issued responses as IDP initiated responses, even though we later also use the gosaml2 library to parse (and then\n\t// also validate) the response _again_. The reason is that the gosaml2 library does not make this information\n\t// easily/publicly accessible through its API.\n\tparsedSamlResponseDocument, _, err := samlUtils.ParseSamlResponse(samlResponse)\n\tif err != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\"could not parse saml response\").WithCause(err),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\tresponseElement := parsedSamlResponseDocument.FindElement(\"/Response\")\n\tif responseElement == nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\"invalid saml response: no response node present\"),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\tissuerElement := parsedSamlResponseDocument.FindElement(\"/Response/Issuer\")\n\tif issuerElement == nil || issuerElement.Text() == \"\" {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\"invalid saml response: no issuer node present\"),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\tissuer := issuerElement.Text()\n\n\tserviceProvider, err := handler.samlService.GetProviderByIssuer(issuer)\n\tif err != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\n\t\t\t\tfmt.Sprintf(\"could not get provider for issuer %s\", issuer)).\n\t\t\t\tWithCause(err),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\t// We need to check whether this is an unsolicited request, otherwise SP initiated responses could\n\t// be used as IDP initiated responses.\n\tif responseElement.SelectAttr(\"InResponseTo\") != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\"saml request is not unsolicited\"),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\tassertionInfo, err := handler.getAssertionInfo(serviceProvider, samlResponse)\n\tif err != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\"could not get assertion info\").WithCause(err),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\tsamlResponseIDAttr := responseElement.SelectAttr(\"ID\")\n\tif samlResponseIDAttr == nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\"invalid saml response: no ID for response present\"),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\tsamlResponseID := samlResponseIDAttr.Value\n\n\tsamlIDPInitiatedRequestPersister := handler.samlService.Persister().GetSamlIDPInitiatedRequestPersister()\n\n\t// We use the SAML response's ID to prevent replay attacks by persisting every IDP initiated request and\n\t// checking whether an IDP initiated request already exists for this request.\n\texistingSamlIDPInitiatedRequest, err := samlIDPInitiatedRequestPersister.GetByResponseIDAndIssuer(samlResponseID, issuer)\n\tif existingSamlIDPInitiatedRequest != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorInvalidRequest(\"attempting to replay unsolicited saml request\"),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\t// We assume only one assertion, and we assume it is present because we already validated it using the gosaml2\n\t// library (which also consumes only one/the first assertion). We also assume assertion conditions are present\n\t// because validation assures it is not nil (or else it returns an error).\n\texpiresAtString := assertionInfo.Assertions[0].Conditions.NotOnOrAfter\n\n\texpiresAt, err := time.Parse(time.RFC3339, expiresAtString)\n\tif err != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorServer(\"could not parse saml assertion conditions' NotOnOrAfter value\").WithCause(err),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\t// If no request exists we create a new IDP initiated request model and persist it.\n\tsamlIDPInitiatedRequest, err := models.NewSamlIDPInitiatedRequest(samlResponseID, issuer, expiresAt)\n\tif err != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorServer(\"could not instantiate saml idp initiated request model\").WithCause(err),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\terr = samlIDPInitiatedRequestPersister.Create(*samlIDPInitiatedRequest)\n\tif err != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tthirdparty.ErrorServer(\"could not persist saml idp initiated request\"),\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\tredirectUrl, samlError := handler.linkAccount(c, redirectTo, true, serviceProvider, assertionInfo)\n\tif samlError != nil {\n\t\treturn handler.redirectError(\n\t\t\tc,\n\t\t\tsamlError,\n\t\t\tredirectTo.String(),\n\t\t)\n\t}\n\n\t// Add hint to the redirect URL that this is an IDP initiated request so that a token exchange can\n\t// eventually be performed through the dedicated flow API handler.\n\tvalues := redirectUrl.Query()\n\tvalues.Add(\"saml_hint\", \"idp_initiated\")\n\tredirectUrl.RawQuery = values.Encode()\n\n\treturn c.Redirect(http.StatusFound, redirectUrl.String())\n}\n\nfunc (handler *Handler) CallbackPost(c echo.Context) error {\n\trelayState := c.FormValue(\"RelayState\")\n\tsamlResponse := c.FormValue(\"SAMLResponse\")\n\n\tif handler.isIDPInitiated(relayState) {\n\t\treturn handler.callbackPostIdPInitiated(c, samlResponse)\n\t} else {\n\t\tstate, err := VerifyState(\n\t\t\thandler.samlService.Config(),\n\t\t\thandler.samlService.Persister().GetSamlStatePersister(),\n\t\t\tstrings.TrimPrefix(relayState, statePrefixServiceProviderInitiated),\n\t\t)\n\n\t\tif err != nil {\n\t\t\treturn handler.redirectError(\n\t\t\t\tc,\n\t\t\t\tthirdparty.ErrorInvalidRequest(err.Error()).WithCause(err),\n\t\t\t\thandler.samlService.Config().Saml.DefaultRedirectUrl,\n\t\t\t)\n\t\t}\n\n\t\tif strings.TrimSpace(state.RedirectTo) == \"\" {\n\t\t\tstate.RedirectTo = handler.samlService.Config().Saml.DefaultRedirectUrl\n\t\t}\n\n\t\tredirectTo, err := url.Parse(state.RedirectTo)\n\t\tif err != nil {\n\t\t\treturn handler.redirectError(\n\t\t\t\tc,\n\t\t\t\tthirdparty.ErrorServer(\"unable to parse redirect url\").WithCause(err),\n\t\t\t\thandler.samlService.Config().Saml.DefaultRedirectUrl,\n\t\t\t)\n\t\t}\n\n\t\tfoundProvider, err := handler.samlService.GetProviderByDomain(state.Provider)\n\t\tif err != nil {\n\t\t\treturn handler.redirectError(\n\t\t\t\tc,\n\t\t\t\tthirdparty.ErrorServer(\"unable to find provider by domain\").WithCause(err),\n\t\t\t\tredirectTo.String(),\n\t\t\t)\n\t\t}\n\n\t\tassertionInfo, err := handler.getAssertionInfo(foundProvider, samlResponse)\n\t\tif err != nil {\n\t\t\treturn handler.redirectError(\n\t\t\t\tc,\n\t\t\t\tthirdparty.ErrorServer(\"unable to parse saml response\").WithCause(err),\n\t\t\t\tredirectTo.String(),\n\t\t\t)\n\t\t}\n\n\t\tredirectUrl, err := handler.linkAccount(c, redirectTo, state.IsFlow, foundProvider, assertionInfo)\n\t\tif err != nil {\n\t\t\treturn handler.redirectError(\n\t\t\t\tc,\n\t\t\t\terr,\n\t\t\t\tredirectTo.String(),\n\t\t\t)\n\t\t}\n\n\t\treturn c.Redirect(http.StatusFound, redirectUrl.String())\n\t}\n}\n\nfunc (handler *Handler) isIDPInitiated(relayState string) bool {\n\treturn !strings.HasPrefix(relayState, statePrefixServiceProviderInitiated)\n}\n\nfunc (handler *Handler) linkAccount(c echo.Context, redirectTo *url.URL, isFlow bool, provider provider.ServiceProvider, assertionInfo *saml2.AssertionInfo) (*url.URL, error) {\n\tvar accountLinkingResult *thirdparty.AccountLinkingResult\n\tvar err error\n\terr = handler.samlService.Persister().Transaction(func(tx *pop.Connection) error {\n\t\tuserdata := provider.GetUserData(assertionInfo)\n\t\tidentityProviderIssuer := assertionInfo.Assertions[0].Issuer\n\t\tsamlDomain := provider.GetDomain()\n\t\tlinkResult, errTx := thirdparty.LinkAccount(tx, handler.samlService.Config(), handler.samlService.Persister(), userdata, identityProviderIssuer.Value, true, &samlDomain, isFlow, nil)\n\t\tif errTx != nil {\n\t\t\treturn errTx\n\t\t}\n\n\t\taccountLinkingResult = linkResult\n\n\t\temailModel := linkResult.User.Emails.GetEmailByAddress(userdata.Metadata.Email)\n\t\tidentityModel := emailModel.Identities.GetIdentity(identityProviderIssuer.Value, userdata.Metadata.Subject)\n\n\t\ttoken, errTx := models.NewToken(\n\t\t\tlinkResult.User.ID,\n\t\t\tmodels.TokenWithIdentityID(identityModel.ID),\n\t\t\tmodels.TokenForFlowAPI(isFlow),\n\t\t\tmodels.TokenUserCreated(linkResult.UserCreated))\n\t\tif errTx != nil {\n\t\t\treturn thirdparty.ErrorServer(\"could not create token\").WithCause(errTx)\n\t\t}\n\n\t\terrTx = handler.samlService.Persister().GetTokenPersisterWithConnection(tx).Create(*token)\n\t\tif errTx != nil {\n\t\t\treturn thirdparty.ErrorServer(\"could not save token to db\").WithCause(errTx)\n\t\t}\n\n\t\tquery := redirectTo.Query()\n\t\tquery.Add(utils.HankoTokenQuery, token.Value)\n\t\tredirectTo.RawQuery = query.Encode()\n\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = handler.auditLogger.Create(c, accountLinkingResult.Type, accountLinkingResult.User, nil)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn redirectTo, nil\n}\n\nfunc (handler *Handler) getAssertionInfo(provider provider.ServiceProvider, samlResponse string) (*saml2.AssertionInfo, error) {\n\tassertionInfo, err := provider.GetService().RetrieveAssertionInfo(samlResponse)\n\tif err != nil {\n\t\treturn nil, thirdparty.ErrorServer(\"unable to parse SAML response\").WithCause(err)\n\t}\n\n\tif assertionInfo.WarningInfo.InvalidTime {\n\t\treturn nil, thirdparty.ErrorServer(\"SAMLAssertion expired\")\n\t}\n\n\tif assertionInfo.WarningInfo.NotInAudience {\n\t\treturn nil, thirdparty.ErrorServer(\"not in SAML audience\")\n\t}\n\n\treturn assertionInfo, nil\n}\n\nfunc (handler *Handler) redirectError(c echo.Context, error error, to string) error {\n\tc.Logger().Error(error)\n\n\terr := handler.auditError(c, error)\n\tif err != nil {\n\t\terror = err\n\t}\n\n\tredirectURL := thirdparty.GetErrorUrl(to, error)\n\treturn c.Redirect(http.StatusSeeOther, redirectURL)\n}\n\nfunc (handler *Handler) auditError(c echo.Context, err error) error {\n\tvar e *thirdparty.ThirdPartyError\n\tok := errors.As(err, &e)\n\n\tvar auditLogError error\n\tif ok && e.Code != thirdparty.ErrorCodeServerError {\n\t\tauditLogError = handler.auditLogger.Create(c, models.AuditLogThirdPartySignInSignUpFailed, nil, err)\n\t}\n\treturn auditLogError\n}\n\nfunc (handler *Handler) GetProvider(c echo.Context) error {\n\tvar request dto.SamlRequest\n\terr := c.Bind(&request)\n\tif err != nil {\n\t\treturn c.JSON(http.StatusBadRequest, err)\n\t}\n\n\tfoundProvider, err := handler.samlService.GetProviderByDomain(request.Domain)\n\tif err != nil {\n\t\treturn c.NoContent(http.StatusNotFound)\n\t}\n\n\treturn c.JSON(http.StatusOK, foundProvider.GetConfig())\n}\n"
  },
  {
    "path": "backend/ee/saml/provider/auth0.go",
    "content": "package provider\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\tsamlConfig \"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n)\n\ntype Auth0Provider struct {\n\t*BaseSamlProvider\n}\n\nfunc NewAuth0ServiceProvider(config *config.Config, idpConfig samlConfig.IdentityProvider, persister persistence.SamlCertificatePersister) (ServiceProvider, error) {\n\tserviceProvider, err := NewBaseSamlProvider(config, idpConfig, persister)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tprovider := &Auth0Provider{\n\t\tserviceProvider.(*BaseSamlProvider),\n\t}\n\tprovider.UseDefaultAttributesIfEmpty()\n\n\treturn provider, nil\n}\n\nfunc (sp *Auth0Provider) UseDefaultAttributesIfEmpty() {\n\tattributeMap := &sp.Config.AttributeMap\n\n\tif attributeMap.Name == \"\" {\n\t\tattributeMap.Name = \"http://schemas.auth0.com/name\"\n\t}\n\n\tif attributeMap.Email == \"\" {\n\t\tattributeMap.Name = \"http://schemas.auth0.com/email\"\n\t}\n\n\tif attributeMap.EmailVerified == \"\" {\n\t\tattributeMap.EmailVerified = \"http://schemas.auth0.com/email_verified\"\n\t}\n\n\tif attributeMap.NickName == \"\" {\n\t\tattributeMap.NickName = \"http://schemas.auth0.com/nickname\"\n\t}\n\n\tif attributeMap.Picture == \"\" {\n\t\tattributeMap.Picture = \"http://schemas.auth0.com/picture\"\n\t}\n\n\tif attributeMap.UpdatedAt == \"\" {\n\t\tattributeMap.UpdatedAt = \"http://schemas.auth0.com/updated_at\"\n\t}\n}\n"
  },
  {
    "path": "backend/ee/saml/provider/provider.go",
    "content": "package provider\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\tsaml2 \"github.com/russellhaering/gosaml2\"\n\t\"github.com/russellhaering/gosaml2/types\"\n\tdsig \"github.com/russellhaering/goxmldsig\"\n\tdsigTypes \"github.com/russellhaering/goxmldsig/types\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\tsamlConfig \"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype IdpMetadata struct {\n\tSingleSignOnUrl string\n\tIssuer          string\n\tcerts           dsig.MemoryX509CertificateStore\n}\n\ntype ServiceProvider interface {\n\tGetUserData(assertion *saml2.AssertionInfo) *thirdparty.UserData\n\tProvideMetadataAsXml() ([]byte, error)\n\tUseDefaultAttributesIfEmpty()\n\tGetDomain() string\n\tGetService() *saml2.SAMLServiceProvider\n\tGetConfig() samlConfig.IdentityProvider\n}\n\nfunc loadCertificate(cfg *config.Config, persister persistence.SamlCertificatePersister) (dsig.X509KeyStore, error) {\n\tcert, err := persister.GetFirst()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cert == nil {\n\t\tcert, err = models.NewSamlCertificate(cfg.Service.Name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\terr = persister.Create(cert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tprivateKey, err := cert.DecryptCertKey()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkeys, err := tls.X509KeyPair([]byte(cert.CertData), privateKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to create key pair: %w\", err)\n\t}\n\n\tkeys.Leaf, err = x509.ParseCertificate(keys.Certificate[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to parse certificate: %w\", err)\n\t}\n\n\treturn dsig.TLSCertKeyStore(keys), nil\n}\n\nfunc fetchIdpMetadata(idpConfig samlConfig.IdentityProvider) (*IdpMetadata, error) {\n\tresponse, err := http.Get(idpConfig.MetadataUrl)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to fetch metadata: %w\", err)\n\t}\n\n\tif response.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"request for idp metadata failed with status code: %v\", response.StatusCode)\n\t}\n\n\tmetadataBody, err := io.ReadAll(response.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to read idp metadata response body: %w\", err)\n\t}\n\n\tidpMetadata := &types.EntityDescriptor{}\n\terr = xml.Unmarshal(metadataBody, idpMetadata)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to unmarshal idp metadata response body to xml: %w\", err)\n\t}\n\n\tidPCertStore := dsig.MemoryX509CertificateStore{\n\t\tRoots: []*x509.Certificate{},\n\t}\n\n\tfor _, keyDescriptor := range idpMetadata.IDPSSODescriptor.KeyDescriptors {\n\t\tfor index, x509Certificate := range keyDescriptor.KeyInfo.X509Data.X509Certificates {\n\t\t\tparsedCert, err := parseCertificate(index, x509Certificate)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tidPCertStore.Roots = append(idPCertStore.Roots, parsedCert)\n\t\t}\n\t}\n\n\treturn &IdpMetadata{\n\t\tSingleSignOnUrl: idpMetadata.IDPSSODescriptor.SingleSignOnServices[0].Location,\n\t\tIssuer:          idpMetadata.EntityID,\n\t\tcerts:           idPCertStore,\n\t}, nil\n}\n\nfunc parseCertificate(index int, x509Certificate dsigTypes.X509Certificate) (*x509.Certificate, error) {\n\tif x509Certificate.Data == \"\" {\n\t\treturn nil, fmt.Errorf(\"metadata contains an empty certificate at index %d\", index)\n\t}\n\n\tstringifiedData := strings.TrimSpace(strings.ReplaceAll(x509Certificate.Data, \"\\n\", \"\"))\n\n\tcertData, err := base64.StdEncoding.DecodeString(stringifiedData)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to decode certificate at index %d: %w\", index, err)\n\t}\n\n\tidpCertificate, err := x509.ParseCertificate(certData)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to parse certificate at index %d: %w\", index, err)\n\t}\n\n\treturn idpCertificate, nil\n}\n\nfunc GetProvider(providerName string, cfg *config.Config, idpConfig samlConfig.IdentityProvider, persister persistence.SamlCertificatePersister) (ServiceProvider, error) {\n\tif strings.Contains(strings.ToLower(providerName), \"auth0\") {\n\t\treturn NewAuth0ServiceProvider(cfg, idpConfig, persister)\n\t}\n\n\treturn NewBaseSamlProvider(cfg, idpConfig, persister)\n}\n"
  },
  {
    "path": "backend/ee/saml/provider/saml.go",
    "content": "package provider\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"github.com/fatih/structs\"\n\tsaml2 \"github.com/russellhaering/gosaml2\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\tsamlConfig \"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype BaseSamlProvider struct {\n\tConfig  samlConfig.IdentityProvider\n\tService saml2.SAMLServiceProvider\n}\n\nfunc NewBaseSamlProvider(cfg *config.Config, idpConfig samlConfig.IdentityProvider, persister persistence.SamlCertificatePersister) (ServiceProvider, error) {\n\tserviceProviderCertStore, err := loadCertificate(cfg, persister)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tidpMetadata, err := fetchIdpMetadata(idpConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tprovider := &BaseSamlProvider{\n\t\tConfig: idpConfig,\n\t\tService: saml2.SAMLServiceProvider{\n\t\t\tIdentityProviderSSOURL: idpMetadata.SingleSignOnUrl,\n\t\t\tIdentityProviderIssuer: idpMetadata.Issuer,\n\t\t\tIDPCertificateStore:    &idpMetadata.certs,\n\n\t\t\tAssertionConsumerServiceURL: fmt.Sprintf(\"%s/saml/callback\", cfg.Saml.Endpoint),\n\t\t\tServiceProviderIssuer:       cfg.Saml.Endpoint,\n\t\t\tServiceProviderSLOURL:       fmt.Sprintf(\"%s/saml/logout\", cfg.Saml.Endpoint),\n\t\t\tSPKeyStore:                  serviceProviderCertStore,\n\n\t\t\tSignAuthnRequests:       cfg.Saml.Options.SignAuthnRequests,\n\t\t\tForceAuthn:              cfg.Saml.Options.ForceLogin,\n\t\t\tIsPassive:               false,\n\t\t\tAudienceURI:             cfg.Saml.AudienceUri,\n\t\t\tValidateEncryptionCert:  cfg.Saml.Options.ValidateEncryptionCertificate,\n\t\t\tSkipSignatureValidation: cfg.Saml.Options.SkipSignatureValidation,\n\t\t\tAllowMissingAttributes:  cfg.Saml.Options.AllowMissingAttributes,\n\t\t},\n\t}\n\tprovider.UseDefaultAttributesIfEmpty()\n\n\treturn provider, nil\n}\n\nfunc (sp *BaseSamlProvider) ProvideMetadataAsXml() ([]byte, error) {\n\tmetadata, err := sp.Service.Metadata()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Workaround as the lib is currently marshalling nanoseconds which cannot be used\n\tmetadata.ValidUntil = time.Now().Add(time.Hour * 24 * 7).Round(time.Millisecond)\n\n\treturn xml.MarshalIndent(metadata, \"\", \"  \")\n}\n\nfunc (sp *BaseSamlProvider) GetUserData(assertionInfo *saml2.AssertionInfo) *thirdparty.UserData {\n\tfirstAssertion := assertionInfo.Assertions[0]\n\tassertionValues := assertionInfo.Values\n\tattributeMap := &sp.Config.AttributeMap\n\n\temailAddress := assertionValues.Get(attributeMap.Email)\n\n\temail := thirdparty.Email{\n\t\tEmail:    emailAddress,\n\t\tVerified: assertionValues.Get(attributeMap.EmailVerified) == \"true\",\n\t\tPrimary:  true,\n\t}\n\n\texpiresIn, _ := time.Parse(time.RFC3339, firstAssertion.Conditions.NotOnOrAfter)\n\n\tuserData := &thirdparty.UserData{}\n\tuserData.Emails = append(userData.Emails, email)\n\n\tuserData.Metadata = &thirdparty.Claims{\n\t\tIssuer:            firstAssertion.Issuer.Value,\n\t\tSubject:           firstAssertion.Subject.NameID.Value,\n\t\tAud:               sp.Service.AudienceURI,\n\t\tIat:               float64(assertionInfo.AuthnInstant.Unix()),\n\t\tExp:               float64(expiresIn.Unix()),\n\t\tName:              assertionValues.Get(attributeMap.Name),\n\t\tFamilyName:        assertionValues.Get(attributeMap.FamilyName),\n\t\tGivenName:         assertionValues.Get(attributeMap.GivenName),\n\t\tMiddleName:        assertionValues.Get(attributeMap.MiddleName),\n\t\tNickName:          assertionValues.Get(attributeMap.NickName),\n\t\tPreferredUsername: assertionValues.Get(attributeMap.PreferredUsername),\n\t\tProfile:           assertionValues.Get(attributeMap.Profile),\n\t\tPicture:           assertionValues.Get(attributeMap.Picture),\n\t\tWebsite:           assertionValues.Get(attributeMap.Website),\n\t\tGender:            assertionValues.Get(attributeMap.Gender),\n\t\tBirthdate:         assertionValues.Get(attributeMap.Birthdate),\n\t\tZoneInfo:          assertionValues.Get(attributeMap.ZoneInfo),\n\t\tLocale:            assertionValues.Get(attributeMap.Locale),\n\t\tUpdatedAt:         assertionValues.Get(attributeMap.UpdatedAt),\n\t\tEmail:             emailAddress,\n\t\tEmailVerified:     email.Verified || sp.Config.SkipEmailVerification,\n\t\tPhone:             assertionValues.Get(attributeMap.Phone),\n\t\tPhoneVerified:     assertionValues.Get(attributeMap.PhoneVerified) != \"\",\n\t\tCustomClaims:      sp.mapCustomClaims(assertionInfo.Values, attributeMap),\n\t}\n\n\treturn userData\n}\n\nfunc (sp *BaseSamlProvider) mapCustomClaims(values saml2.Values, attributeMap *samlConfig.AttributeMap) map[string]interface{} {\n\tcustomAttributes := make(map[string]interface{})\n\n\ts := structs.New(attributeMap)\n\tfor name := range values {\n\t\thasField := false\n\t\tfor _, field := range s.Fields() {\n\t\t\tif field.Value().(string) == name {\n\t\t\t\thasField = true\n\t\t\t}\n\t\t}\n\n\t\tif !hasField {\n\t\t\tcustomAttributes[name] = values.Get(name)\n\t\t}\n\t}\n\n\treturn customAttributes\n}\n\nfunc (sp *BaseSamlProvider) UseDefaultAttributesIfEmpty() {\n\tattributeMap := &sp.Config.AttributeMap\n\tif strings.TrimSpace(attributeMap.Name) == \"\" {\n\t\tattributeMap.Name = \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\"\n\t}\n\n\tif strings.TrimSpace(attributeMap.GivenName) == \"\" {\n\t\tattributeMap.GivenName = \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname\"\n\t}\n\n\tif strings.TrimSpace(attributeMap.FamilyName) == \"\" {\n\t\tattributeMap.FamilyName = \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname\"\n\t}\n\n\tif strings.TrimSpace(attributeMap.Email) == \"\" {\n\t\tattributeMap.Email = \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"\n\t}\n}\n\nfunc (sp *BaseSamlProvider) GetDomain() string {\n\treturn sp.Config.Domain\n}\n\nfunc (sp *BaseSamlProvider) GetService() *saml2.SAMLServiceProvider {\n\treturn &sp.Service\n}\n\nfunc (sp *BaseSamlProvider) GetConfig() samlConfig.IdentityProvider {\n\treturn sp.Config\n}\n"
  },
  {
    "path": "backend/ee/saml/router.go",
    "content": "package saml\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\nfunc CreateSamlRoutes(e *echo.Echo, sessionManager session.Manager, auditLogger auditlog.Logger, samlService Service) {\n\thandler := NewSamlHandler(sessionManager, auditLogger, samlService)\n\troutingGroup := e.Group(\"saml\")\n\troutingGroup.GET(\"/provider\", handler.GetProvider)\n\troutingGroup.GET(\"/metadata\", handler.Metadata)\n\troutingGroup.GET(\"/auth\", handler.Auth)\n\troutingGroup.POST(\"/callback\", handler.CallbackPost)\n}\n"
  },
  {
    "path": "backend/ee/saml/service.go",
    "content": "package saml\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml/provider\"\n\tsamlUtils \"github.com/teamhanko/hanko/backend/v2/ee/saml/utils\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"net/url\"\n)\n\ntype Service interface {\n\tConfig() *config.Config\n\tPersister() persistence.Persister\n\tProviders() []provider.ServiceProvider\n\tGetProviderByDomain(domain string) (provider.ServiceProvider, error)\n\tGetProviderByIssuer(issuer string) (provider.ServiceProvider, error)\n\tGetAuthUrl(provider provider.ServiceProvider, redirectTo string, isFlow bool) (string, error)\n}\n\ntype defaultService struct {\n\tconfig    *config.Config\n\tpersister persistence.Persister\n\tproviders []provider.ServiceProvider\n}\n\nfunc NewSamlService(cfg *config.Config, persister persistence.Persister) Service {\n\tproviders := make([]provider.ServiceProvider, 0)\n\tfor _, idpConfig := range cfg.Saml.IdentityProviders {\n\t\tif idpConfig.Enabled {\n\t\t\thostName := \"\"\n\t\t\thostName, err := parseProviderFromMetadataUrl(idpConfig.MetadataUrl)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"failed to parse provider '%s' from metadata url: %v\\n\", idpConfig.Name, err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tnewProvider, err := provider.GetProvider(hostName, cfg, idpConfig, persister.GetSamlCertificatePersister())\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"failed to initialize provider '%s': %v\\n\", idpConfig.Name, err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tproviders = append(providers, newProvider)\n\t\t}\n\t}\n\n\treturn &defaultService{\n\t\tconfig:    cfg,\n\t\tpersister: persister,\n\t\tproviders: providers,\n\t}\n}\n\nfunc parseProviderFromMetadataUrl(idpUrlString string) (string, error) {\n\tidpUrl, err := url.Parse(idpUrlString)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn idpUrl.Host, nil\n}\n\nfunc (s *defaultService) Config() *config.Config {\n\treturn s.config\n}\n\nfunc (s *defaultService) Persister() persistence.Persister {\n\treturn s.persister\n}\n\nfunc (s *defaultService) Providers() []provider.ServiceProvider {\n\treturn s.providers\n}\n\nfunc (s *defaultService) GetProviderByDomain(domain string) (provider.ServiceProvider, error) {\n\tfor _, availableProvider := range s.providers {\n\t\tif availableProvider.GetDomain() == domain {\n\t\t\treturn availableProvider, nil\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"unknown provider for domain %s\", domain)\n}\n\nfunc (s *defaultService) GetProviderByIssuer(issuer string) (provider.ServiceProvider, error) {\n\tfor _, availableProvider := range s.providers {\n\t\tif availableProvider.GetService().IdentityProviderIssuer == issuer {\n\t\t\treturn availableProvider, nil\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"unknown provider for issuer %s\", issuer)\n}\n\nfunc (s *defaultService) GetAuthUrl(provider provider.ServiceProvider, redirectTo string, isFlow bool) (string, error) {\n\tif ok := samlUtils.IsAllowedRedirect(s.config.Saml, redirectTo); !ok {\n\t\treturn \"\", thirdparty.ErrorInvalidRequest(fmt.Sprintf(\"redirect to '%s' not allowed\", redirectTo))\n\t}\n\n\tstate, err := GenerateState(\n\t\ts.config,\n\t\ts.persister.GetSamlStatePersister(),\n\t\tprovider.GetDomain(),\n\t\tredirectTo,\n\t\tGenerateStateForFlowAPI(isFlow))\n\n\tif err != nil {\n\t\treturn \"\", thirdparty.ErrorServer(\"could not generate state\").WithCause(err)\n\t}\n\n\tredirectUrl, err := provider.GetService().BuildAuthURL(string(state))\n\tif err != nil {\n\t\treturn \"\", thirdparty.ErrorServer(\"could not generate auth url\").WithCause(err)\n\t}\n\n\treturn redirectUrl, nil\n}\n"
  },
  {
    "path": "backend/ee/saml/state.go",
    "content": "package saml\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/aes_gcm\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype State struct {\n\tProvider   string    `json:\"provider\"`\n\tRedirectTo string    `json:\"redirect_to\"`\n\tIssuedAt   time.Time `json:\"issued_at\"`\n\tExpiresAt  time.Time `json:\"expires_at\"`\n\tNonce      string    `json:\"nonce\"`\n\tIsFlow     bool      `json:\"is_flow\"`\n}\n\nconst statePrefixServiceProviderInitiated = \"hanko_spi_\"\n\nfunc GenerateStateForFlowAPI(isFlow bool) func(*State) {\n\treturn func(state *State) {\n\t\tstate.IsFlow = isFlow\n\t}\n}\n\nfunc GenerateState(config *config.Config, persister persistence.SamlStatePersister, provider string, redirectTo string, options ...func(*State)) ([]byte, error) {\n\tif strings.TrimSpace(provider) == \"\" {\n\t\treturn nil, errors.New(\"provider must be present\")\n\t}\n\n\tif strings.TrimSpace(redirectTo) == \"\" {\n\t\tredirectTo = config.Saml.DefaultRedirectUrl\n\t}\n\n\tnonce, err := crypto.GenerateRandomStringURLSafe(32)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not generate nonce: %w\", err)\n\t}\n\n\tnow := time.Now().UTC()\n\tstate := State{\n\t\tProvider:   provider,\n\t\tRedirectTo: redirectTo,\n\t\tIssuedAt:   now,\n\t\tExpiresAt:  now.Add(time.Minute * 5),\n\t\tNonce:      nonce,\n\t}\n\n\tfor _, option := range options {\n\t\toption(&state)\n\t}\n\n\tstateJson, err := json.Marshal(state)\n\n\taes, err := aes_gcm.NewAESGCM(config.Secrets.Keys)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not instantiate aesgcm: %w\", err)\n\t}\n\n\tencryptedState, err := aes.Encrypt(stateJson)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not encrypt state: %w\", err)\n\t}\n\n\tdbState, err := models.NewSamlState(nonce, encryptedState)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not create state model: %w\", err)\n\t}\n\n\terr = persister.Create(*dbState)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not save state to db: %w\", err)\n\t}\n\n\t// Add prefix to distinguish between SP initiated and IDP initiated requests in callback handler.\n\tresult := fmt.Sprintf(\"%s%s\", statePrefixServiceProviderInitiated, encryptedState)\n\treturn []byte(result), nil\n}\n\nfunc VerifyState(config *config.Config, persister persistence.SamlStatePersister, state string) (*State, error) {\n\tdecodedState, err := decodeState(config, state)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not decode state: %w\", err)\n\t}\n\n\texpectedState, err := persister.GetByNonce(decodedState.Nonce)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not fetch expected state from db: %w\", err)\n\t}\n\n\tdecodedExpectedState, err := decodeState(config, expectedState.State)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not decode expectedState: %w\", err)\n\t}\n\n\tif decodedState.Nonce != decodedExpectedState.Nonce {\n\t\treturn nil, errors.New(\"could not verify state\")\n\t}\n\n\t_ = persister.Delete(*expectedState)\n\n\tif time.Now().UTC().After(decodedState.ExpiresAt) {\n\t\treturn nil, errors.New(\"state is expired\")\n\t}\n\n\treturn decodedState, nil\n}\n\nfunc decodeState(config *config.Config, state string) (*State, error) {\n\taes, err := aes_gcm.NewAESGCM(config.Secrets.Keys)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not instantiate aesgcm: %w\", err)\n\t}\n\n\tdecryptedState, err := aes.Decrypt(state)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not decrypt state: %w\", err)\n\t}\n\n\tvar unmarshalledState State\n\terr = json.Unmarshal(decryptedState, &unmarshalledState)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not unmarshal state: %w\", err)\n\t}\n\n\treturn &unmarshalledState, nil\n}\n"
  },
  {
    "path": "backend/ee/saml/state_test.go",
    "content": "package saml\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\tsamlConfig \"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSamlSuite(t *testing.T) {\n\ts := new(samlSuite)\n\tsuite.Run(t, s)\n}\n\ntype samlSuite struct {\n\ttest.Suite\n}\n\nfunc (s *samlSuite) TestSaml_GenerateState() {\n\tcfg := &config.Config{\n\t\tSecrets: config.Secrets{Keys: []string{\"thirty-two-byte-long-test-secret\"}},\n\t\tSaml: samlConfig.Saml{\n\t\t\tDefaultRedirectUrl: \"https://example.com\",\n\t\t},\n\t}\n\n\tpersister := s.Storage.GetSamlStatePersister()\n\n\tstate, err := GenerateState(cfg, persister, \"test-provider\", \"https://example.com\")\n\tassert.NoError(s.T(), err)\n\tassert.NotNil(s.T(), state)\n}\n\nfunc (s *samlSuite) TestSaml_GenerateStateWithDefaultRedirect() {\n\tcfg := &config.Config{\n\t\tSecrets: config.Secrets{Keys: []string{\"thirty-two-byte-long-test-secret\"}},\n\t\tSaml: samlConfig.Saml{\n\t\t\tDefaultRedirectUrl: \"https://example.com\",\n\t\t},\n\t}\n\n\tpersister := s.Storage.GetSamlStatePersister()\n\n\tstate, err := GenerateState(cfg, persister, \"test-provider\", \"\")\n\tassert.NoError(s.T(), err)\n\tassert.NotNil(s.T(), state)\n}\n\nfunc (s *samlSuite) TestSaml_GenerateState_Error() {\n\n\tredirectTo := \"https://example.com\"\n\n\ttests := []struct {\n\t\tname             string\n\t\tprovider         string\n\t\tredirectTo       string\n\t\tsecret           string\n\t\terrorRedirectUrl string\n\t\texpectedError    string\n\t}{\n\t\t{\n\t\t\tname:             \"provider is not present\",\n\t\t\tsecret:           \"test-secret\",\n\t\t\tprovider:         \"\",\n\t\t\tredirectTo:       redirectTo,\n\t\t\terrorRedirectUrl: redirectTo,\n\t\t\texpectedError:    \"provider must be present\",\n\t\t},\n\t\t{\n\t\t\tname:             \"provider is not present\",\n\t\t\tsecret:           \"\",\n\t\t\tprovider:         \"test-provider\",\n\t\t\tredirectTo:       redirectTo,\n\t\t\terrorRedirectUrl: redirectTo,\n\t\t\texpectedError:    \"could not instantiate aesgcm\",\n\t\t},\n\t}\n\tfor _, testData := range tests {\n\t\ts.T().Run(testData.name, func(t *testing.T) {\n\t\t\tcfg := &config.Config{\n\t\t\t\tSecrets: config.Secrets{Keys: []string{testData.secret}},\n\t\t\t}\n\n\t\t\tpersister := s.Storage.GetSamlStatePersister()\n\n\t\t\t_, err := GenerateState(cfg, persister, testData.provider, testData.redirectTo)\n\t\t\tassert.NotNil(t, err)\n\t\t\tassert.True(t, strings.Contains(err.Error(), testData.expectedError))\n\t\t})\n\t}\n}\n\nfunc (s *samlSuite) TestSaml_VerifyState() {\n\tcfg := &config.Config{\n\t\tSecrets: config.Secrets{Keys: []string{\"thirty-two-byte-long-test-secret\"}},\n\t}\n\n\terr := s.LoadFixtures(\"../../test/fixtures/saml_state\")\n\ts.Require().NoError(err)\n\n\tstate := \"HmD7wlGQ7bF_4MGtmFRQuuSGTshHETDs4RQa64JAx-6EsmNsUjaQwYNOnjWUs6qIOuQMBTKapDGVXVCk00pX2vSS-x-WVqdzZ8KyeQ-9IHu2mwb-AeRbb2QPE-GFnvp2wrbCskKvWvtOfipyeTsnYY5iM90DxssaUtvKnawaB5_MNNekfKyiOeepIkKjUfSJ6-yTR7AAA4B9jwOfDRB4zdV8kKPVJlGVBJFosL11YWJaLxRGQR69nah3Jf9Z6bSAGXxWp24PoBYhij-dH4JyDCcU7D-NeT2A8qFFFjQ1m28C8fsr6zqb4w==\"\n\n\tpersister := s.Storage.GetSamlStatePersister()\n\tvalidState, err := VerifyState(cfg, persister, state)\n\trequire.NoError(s.T(), err)\n\trequire.NotNil(s.T(), validState)\n\trequire.Equal(s.T(), \"test-provider\", validState.Provider)\n\trequire.Equal(s.T(), \"https://example.com\", validState.RedirectTo)\n\trequire.NotEmpty(s.T(), validState.Nonce)\n\trequire.True(s.T(), time.Now().UTC().Before(validState.ExpiresAt))\n}\n\nfunc (s *samlSuite) TestSaml_VerifyState_Error() {\n\terr := s.LoadFixtures(\"../../test/fixtures/saml_state\")\n\ts.Require().NoError(err)\n\n\ttests := []struct {\n\t\tname          string\n\t\tstate         string\n\t\texpectedError string\n\t}{\n\t\t{\n\t\t\tname:          \"error on invalid state\",\n\t\t\tstate:         \"invalid_state\",\n\t\t\texpectedError: \"could not decode state\",\n\t\t},\n\t\t{\n\t\t\tname:          \"error on invalid expected state\",\n\t\t\tstate:         \"ctj9hAU6kFkdc-g5ZXWGHbbnE1NSoQ55afdcYWLImTc5C5gGdeaknPDvZ560LJ07uA8I7X2ssPBKkkQb5xDnDHykmQtEp1hBp0uP_PdQdNJZ7FVXb0MpVWMrCv9nZp_fquLQvzjdm2nGRP9VKPt6S_7XNqg_mMA_ri9g7XWuNgJUqUE0PW7TGB9otsD2E7FQNowAl_vY-q0mYYIdkl0qb3lxWxnSzP51ewVv5VTeI7Mfr1gXyxWyTdqTnl_s5azaRq6w8J2SSX1_GTppX49zqCidPVIqYp6IyvotyOz-ePmvTg7tizBsaQ==\",\n\t\t\texpectedError: \"could not decode expectedState\",\n\t\t},\n\t\t{\n\t\t\tname:          \"error on nonce mismatch\",\n\t\t\tstate:         \"Fs1SEytL1YuZJvBCyfF3Iydpl3-aC95bDsIb3VIcbq3sZjhn6iQaNFVNQZG1Maz2bI7Zkz3UvEIrIFohIotDqoR057mBnDKHVeg-TZKLXfviPJqfkOldfNPTpJgO9e4biYGxPx6qkmabk83eO77Qe3rJ2XM6FznQqPoMjq1vOBBJXjBUPeu3KtF1l7ONpHHVCV1Sr0cm0qQ4q5nPYgjI227AOa_MOOIDKfwqxb-jQT_9n0vLtVen9aYFbr_i_57r5aC3nCxnBgq7gXDq7z-E5NDRW23E0x-5CpQdDhElmtoeu4XRx1jOfw==\",\n\t\t\texpectedError: \"could not fetch expected state from db\",\n\t\t},\n\t\t{\n\t\t\tname:          \"error on expired state\",\n\t\t\tstate:         \"UHPShpOq0byNI-A1vvUROLsPjNhGF5Xxdme4llZrnCvXfyZP_RnhRq490XPqKGKeWa621MtAUsV7N6C4OGx-EXK1TaLWNIYfFmByIEIlPSkpMVTFEUedY5UaFXwhWhuQb4Ci0r3QlPCLmQnPin1O4Vb59K2KieJwTvVnZzY3Y3Avj-D91acTMRGN6OabuIDDOH4nl0qDJABkZ6tnYk735ot8s3oyvJmZgmsW0qLOC_OoNMGZqLUTQzCrEayJ-gTXKr6HSvClN-4KGi4htHHLwYjrSCr_6tnQxDhZvNq7GKgXUEXfeUmZGQ==\",\n\t\t\texpectedError: \"state is expired\",\n\t\t},\n\t}\n\tfor _, testData := range tests {\n\t\ts.T().Run(testData.name, func(t *testing.T) {\n\t\t\tcfg := &config.Config{\n\t\t\t\tSecrets: config.Secrets{Keys: []string{\"thirty-two-byte-long-test-secret\"}},\n\t\t\t}\n\n\t\t\tpersister := s.Storage.GetSamlStatePersister()\n\n\t\t\t_, err := VerifyState(cfg, persister, testData.state)\n\t\t\tassert.NotNil(s.T(), err)\n\t\t\tassert.True(s.T(), strings.Contains(err.Error(), testData.expectedError))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/ee/saml/utils/response.go",
    "content": "package utils\n\nimport (\n\t\"bytes\"\n\t\"compress/flate\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"github.com/beevik/etree\"\n\trtvalidator \"github.com/mattermost/xml-roundtrip-validator\"\n\t\"io\"\n)\n\nconst (\n\tdefaultMaxDecompressedResponseSize = 5 * 1024 * 1024\n)\n\nfunc maybeDeflate(data []byte, maxSize int64, decoder func([]byte) error) error {\n\terr := decoder(data)\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\t// Default to 5MB max size\n\tif maxSize == 0 {\n\t\tmaxSize = defaultMaxDecompressedResponseSize\n\t}\n\n\tlr := io.LimitReader(flate.NewReader(bytes.NewReader(data)), maxSize+1)\n\n\tdeflated, err := io.ReadAll(lr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif int64(len(deflated)) > maxSize {\n\t\treturn fmt.Errorf(\"deflated response exceeds maximum size of %d bytes\", maxSize)\n\t}\n\n\treturn decoder(deflated)\n}\n\nfunc ParseSamlResponse(samlResponse string) (*etree.Document, *etree.Element, error) {\n\traw, err := base64.StdEncoding.DecodeString(samlResponse)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"could not decode saml response: %w\", err)\n\t}\n\n\treturn parseResponseXml(raw)\n}\n\nfunc parseResponseXml(xml []byte) (*etree.Document, *etree.Element, error) {\n\tvar doc *etree.Document\n\tvar rawXML []byte\n\n\terr := maybeDeflate(xml, defaultMaxDecompressedResponseSize, func(xml []byte) error {\n\t\tdoc = etree.NewDocument()\n\t\trawXML = xml\n\t\treturn doc.ReadFromBytes(xml)\n\t})\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tel := doc.Root()\n\tif el == nil {\n\t\treturn nil, nil, fmt.Errorf(\"unable to parse response\")\n\t}\n\n\t// Examine the response for attempts to exploit weaknesses in Go's encoding/xml\n\terr = rtvalidator.Validate(bytes.NewReader(rawXML))\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn doc, el, nil\n}\n"
  },
  {
    "path": "backend/ee/saml/utils/url.go",
    "content": "package utils\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n\t\"strings\"\n)\n\nfunc IsAllowedRedirect(config config.Saml, redirectTo string) bool {\n\tif redirectTo == \"\" {\n\t\treturn false\n\t}\n\n\tredirectTo = strings.TrimSuffix(redirectTo, \"/\")\n\n\tfor _, pattern := range config.AllowedRedirectURLMap {\n\t\tif pattern.Match(redirectTo) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "backend/flow_api/flow/capabilities/action_send_capabilities.go",
    "content": "package capabilities\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype RegisterClientCapabilities struct {\n\tshared.Action\n}\n\nfunc (a RegisterClientCapabilities) GetName() flowpilot.ActionName {\n\treturn shared.ActionRegisterClientCapabilities\n}\n\nfunc (a RegisterClientCapabilities) GetDescription() string {\n\treturn \"Send the computers capabilities.\"\n}\n\nfunc (a RegisterClientCapabilities) Initialize(c flowpilot.InitializationContext) {\n\tc.AddInputs(flowpilot.BooleanInput(\"webauthn_conditional_mediation_available\").Hidden(true))\n\tc.AddInputs(flowpilot.BooleanInput(\"webauthn_platform_authenticator_available\").Hidden(true))\n\tc.AddInputs(flowpilot.BooleanInput(\"webauthn_available\").Required(true).Hidden(true))\n}\n\nfunc (a RegisterClientCapabilities) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\twebauthnAvailable := c.Input().Get(\"webauthn_available\").Bool()\n\terr := c.Stash().Set(shared.StashPathWebauthnAvailable, webauthnAvailable)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tconditionalMediationAvailable := c.Input().Get(\"webauthn_conditional_mediation_available\").Bool()\n\terr = c.Stash().Set(shared.StashPathWebauthnConditionalMediationAvailable, conditionalMediationAvailable)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tplatformAuthenticatorAvailable := c.Input().Get(\"webauthn_platform_authenticator_available\").Bool()\n\terr = c.Stash().Set(shared.StashPathWebauthnPlatformAuthenticatorAvailable, platformAuthenticatorAvailable)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tattachmentSupported := platformAuthenticatorAvailable ||\n\t\tdeps.Cfg.MFA.SecurityKeys.AuthenticatorAttachment != \"platform\"\n\terr = c.Stash().Set(shared.StashPathSecurityKeyAttachmentSupported, attachmentSupported)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_continue_to_passkey.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToPasskey struct {\n\tshared.Action\n}\n\nfunc (a ContinueToPasskey) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToPasskeyRegistration\n}\n\nfunc (a ContinueToPasskey) GetDescription() string {\n\treturn \"Register a WebAuthn credential\"\n}\n\nfunc (a ContinueToPasskey) Initialize(_ flowpilot.InitializationContext) {}\n\nfunc (a ContinueToPasskey) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue(shared.StateOnboardingCreatePasskey)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_continue_to_password.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToPassword struct {\n\tshared.Action\n}\n\nfunc (a ContinueToPassword) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToPasswordRegistration\n}\n\nfunc (a ContinueToPassword) GetDescription() string {\n\treturn \"Register a password credential\"\n}\n\nfunc (a ContinueToPassword) Initialize(_ flowpilot.InitializationContext) {}\n\nfunc (a ContinueToPassword) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue(shared.StatePasswordCreation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_register_password.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"unicode/utf8\"\n)\n\ntype RegisterPassword struct {\n\tshared.Action\n}\n\nfunc (a RegisterPassword) GetName() flowpilot.ActionName {\n\treturn shared.ActionRegisterPassword\n}\n\nfunc (a RegisterPassword) GetDescription() string {\n\treturn \"Submit a new password.\"\n}\n\nfunc (a RegisterPassword) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tinput := flowpilot.PasswordInput(\"new_password\").\n\t\tRequired(!deps.Cfg.Password.Optional).\n\t\tMinLength(deps.Cfg.Password.MinLength)\n\n\tc.AddInputs(input)\n}\n\nfunc (a RegisterPassword) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tnewPassword := c.Input().Get(\"new_password\").String()\n\tnewPasswordBytes := []byte(newPassword)\n\n\tif utf8.RuneCountInString(newPassword) < deps.Cfg.Password.MinLength {\n\t\tc.Input().SetError(\"new_password\", flowpilot.ErrorValueInvalid)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\thashedPassword, err := bcrypt.GenerateFromPassword(newPasswordBytes, 12)\n\tif err != nil {\n\t\tif errors.Is(err, bcrypt.ErrPasswordTooLong) {\n\t\t\tc.Input().SetError(\"new_password\", flowpilot.ErrorValueTooLong)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t}\n\t\treturn fmt.Errorf(\"failed to hash password: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathNewPassword, string(hashedPassword))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set new_password to stash: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserHasPassword, true)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_has_password to the stash: %w\", err)\n\t}\n\n\tc.PreventRevert()\n\n\tif err = c.ExecuteHook(shared.ScheduleMFACreationStates{}); err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_skip_method_chooser.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype SkipCredentialOnboardingMethodChooser struct {\n\tshared.Action\n}\n\nfunc (a SkipCredentialOnboardingMethodChooser) GetName() flowpilot.ActionName {\n\treturn shared.ActionSkip\n}\n\nfunc (a SkipCredentialOnboardingMethodChooser) GetDescription() string {\n\treturn \"Skip\"\n}\n\nfunc (a SkipCredentialOnboardingMethodChooser) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\temailExists := c.Stash().Get(shared.StashPathEmail).Exists()\n\n\tif c.GetFlowName() == shared.FlowRegistration &&\n\t\t!(deps.Cfg.Email.UseForAuthentication && emailExists) {\n\t\tc.SuspendAction()\n\t}\n\n\tif !deps.Cfg.Password.Optional && !deps.Cfg.Passkey.Optional {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a SkipCredentialOnboardingMethodChooser) Execute(c flowpilot.ExecutionContext) error {\n\tc.PreventRevert()\n\n\tif err := c.ExecuteHook(shared.ScheduleMFACreationStates{}); err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_skip_passkey.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype SkipPasskey struct {\n\tshared.Action\n}\n\nfunc (a SkipPasskey) GetName() flowpilot.ActionName {\n\treturn shared.ActionSkip\n}\n\nfunc (a SkipPasskey) GetDescription() string {\n\treturn \"Skip\"\n}\n\nfunc (a SkipPasskey) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\temailExists := c.Stash().Get(shared.StashPathEmail).Exists()\n\tcanLoginWithEmail := emailExists &&\n\t\tdeps.Cfg.Email.Enabled &&\n\t\tdeps.Cfg.Email.UseForAuthentication &&\n\t\tdeps.Cfg.Email.UseAsLoginIdentifier\n\n\tif !deps.Cfg.Passkey.Optional {\n\t\tc.SuspendAction()\n\t}\n\n\tif c.IsPreviousState(shared.StateCredentialOnboardingChooser) {\n\t\tc.SuspendAction()\n\t}\n\n\tif c.IsPreviousState(shared.StatePasswordCreation) &&\n\t\t!c.Stash().Get(shared.StashPathUserHasPassword).Bool() &&\n\t\t!canLoginWithEmail {\n\t\tc.SuspendAction()\n\t}\n\n\tif (c.IsPreviousState(shared.StatePasscodeConfirmation) || c.IsPreviousState(shared.StateRegistrationInit)) &&\n\t\ta.acquirePassword(c, \"never\") &&\n\t\t!canLoginWithEmail {\n\t\tc.SuspendAction()\n\t}\n\n}\nfunc (a SkipPasskey) Execute(c flowpilot.ExecutionContext) error {\n\tif a.acquirePassword(c, \"conditional\") &&\n\t\t!c.Stash().Get(shared.StashPathUserHasPassword).Bool() {\n\t\treturn c.Continue(shared.StatePasswordCreation)\n\t}\n\n\tif err := c.ExecuteHook(shared.ScheduleMFACreationStates{}); err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n\nfunc (a SkipPasskey) acquirePassword(c flowpilot.Context, acquireType string) bool {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Password.Enabled {\n\t\treturn false\n\t}\n\n\tif c.IsFlow(shared.FlowLogin) && deps.Cfg.Password.AcquireOnLogin == acquireType {\n\t\treturn true\n\t}\n\n\tif c.IsFlow(shared.FlowRegistration) && deps.Cfg.Password.AcquireOnRegistration == acquireType {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_skip_password.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype SkipPassword struct {\n\tshared.Action\n}\n\nfunc (a SkipPassword) GetName() flowpilot.ActionName {\n\treturn shared.ActionSkip\n}\n\nfunc (a SkipPassword) GetDescription() string {\n\treturn \"Skip\"\n}\n\nfunc (a SkipPassword) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\temailExists := c.Stash().Get(shared.StashPathEmail).Exists()\n\tcanLoginWithEmail := emailExists &&\n\t\tdeps.Cfg.Email.Enabled &&\n\t\tdeps.Cfg.Email.UseForAuthentication &&\n\t\tdeps.Cfg.Email.UseAsLoginIdentifier\n\n\tif !deps.Cfg.Password.Optional {\n\t\tc.SuspendAction()\n\t}\n\n\tif c.IsPreviousState(shared.StateCredentialOnboardingChooser) {\n\t\tc.SuspendAction()\n\t}\n\n\tif c.IsPreviousState(shared.StateOnboardingCreatePasskey) &&\n\t\t!c.Stash().Get(shared.StashPathUserHasWebauthnCredential).Bool() &&\n\t\t!canLoginWithEmail {\n\t\tc.SuspendAction()\n\t}\n\n\tif (c.IsPreviousState(shared.StatePasscodeConfirmation) || c.IsPreviousState(shared.StateRegistrationInit)) &&\n\t\ta.acquirePasskey(c, \"never\") &&\n\t\t!canLoginWithEmail {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a SkipPassword) Execute(c flowpilot.ExecutionContext) error {\n\tif a.acquirePasskey(c, \"conditional\") &&\n\t\t!c.Stash().Get(shared.StashPathUserHasWebauthnCredential).Bool() &&\n\t\tc.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\treturn c.Continue(shared.StateOnboardingCreatePasskey)\n\t}\n\n\tif err := c.ExecuteHook(shared.ScheduleMFACreationStates{}); err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n\nfunc (a SkipPassword) acquirePasskey(c flowpilot.Context, acquireType string) bool {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Passkey.Enabled {\n\t\treturn false\n\t}\n\n\tif c.IsFlow(shared.FlowLogin) && deps.Cfg.Passkey.AcquireOnLogin == acquireType {\n\t\treturn true\n\t}\n\n\tif c.IsFlow(shared.FlowRegistration) && deps.Cfg.Passkey.AcquireOnRegistration == acquireType {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_webauthn_generate_creation_options.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype WebauthnGenerateCreationOptions struct {\n\tshared.Action\n}\n\nfunc (a WebauthnGenerateCreationOptions) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnGenerateCreationOptions\n}\n\nfunc (a WebauthnGenerateCreationOptions) GetDescription() string {\n\treturn \"Get creation options to create a webauthn credential.\"\n}\n\nfunc (a WebauthnGenerateCreationOptions) Initialize(c flowpilot.InitializationContext) {\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a WebauthnGenerateCreationOptions) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif !c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\treturn errors.New(\"user_id does not exist in the stash\")\n\t}\n\n\tif !c.Stash().Get(shared.StashPathEmail).Exists() && !c.Stash().Get(shared.StashPathUsername).Exists() {\n\t\treturn errors.New(\"either email or username must exist in the stash\")\n\t}\n\n\tuserID, err := uuid.FromString(c.Stash().Get(shared.StashPathUserID).String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse user id as a uuid: %w\", err)\n\t}\n\n\temail := c.Stash().Get(shared.StashPathEmail).String()\n\tusername := c.Stash().Get(shared.StashPathUsername).String()\n\n\tparams := services.GenerateCreationOptionsParams{\n\t\tTx:       deps.Tx,\n\t\tUserID:   userID,\n\t\tEmail:    &email,\n\t\tUsername: &username,\n\t}\n\n\tsessionDataModel, creationOptions, err := deps.WebauthnService.GenerateCreationOptionsPasskey(params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate webauthn creation options: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Stash().Set(shared.StashPathCreateMFAOnlyCredential, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Payload().Set(\"creation_options\", creationOptions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue(shared.StateOnboardingVerifyPasskeyAttestation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_webauthn_verify_attestation_response.go",
    "content": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype WebauthnVerifyAttestationResponse struct {\n\tshared.Action\n}\n\nfunc (a WebauthnVerifyAttestationResponse) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnVerifyAttestationResponse\n}\n\nfunc (a WebauthnVerifyAttestationResponse) GetDescription() string {\n\treturn \"Send the result which was generated by creating a webauthn credential.\"\n}\n\nfunc (a WebauthnVerifyAttestationResponse) Initialize(c flowpilot.InitializationContext) {\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\tc.SuspendAction()\n\t}\n\n\tc.AddInputs(flowpilot.JSONInput(\"public_key\"))\n}\n\nfunc (a WebauthnVerifyAttestationResponse) Execute(c flowpilot.ExecutionContext) error {\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif err := c.ExecuteHook(shared.VerifyAttestationResponse{}); err != nil {\n\t\treturn err\n\t}\n\n\tc.PreventRevert()\n\n\tif err := c.ExecuteHook(shared.ScheduleMFACreationStates{}); err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_to_passcode_confirmation.go",
    "content": "package credential_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToPasscodeConfirmation struct {\n\tshared.Action\n}\n\nfunc (a ContinueToPasscodeConfirmation) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToPasscodeConfirmation\n}\n\nfunc (a ContinueToPasscodeConfirmation) GetDescription() string {\n\treturn \"Send a login passcode code via email.\"\n}\n\nfunc (a ContinueToPasscodeConfirmation) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\tif deps.Cfg.Privacy.OnlyShowActualLoginMethods && (!c.Stash().Get(shared.StashPathUserHasEmails).Bool() || !deps.Cfg.Email.Enabled || (deps.Cfg.Email.Enabled && !deps.Cfg.Email.UseForAuthentication)) {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToPasscodeConfirmation) Execute(c flowpilot.ExecutionContext) error {\n\tif err := c.Stash().Set(shared.StashPathLoginMethod, \"passcode\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to set login_method to stash: %w\", err)\n\t}\n\n\tif len(c.Stash().Get(shared.StashPathUserID).String()) > 0 {\n\t\tif err := c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateLogin); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t}\n\t} else {\n\t\tif err := c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailLoginAttempted); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t}\n\t}\n\n\treturn c.Continue(shared.StatePasscodeConfirmation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_to_passcode_confirmation_recovery.go",
    "content": "package credential_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToPasscodeConfirmationRecovery struct {\n\tshared.Action\n}\n\nfunc (a ContinueToPasscodeConfirmationRecovery) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToPasscodeConfirmationRecovery\n}\n\nfunc (a ContinueToPasscodeConfirmationRecovery) GetDescription() string {\n\treturn \"Send a recovery passcode code via email.\"\n}\n\nfunc (a ContinueToPasscodeConfirmationRecovery) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Password.Recovery || len(c.Stash().Get(shared.StashPathEmail).String()) == 0 {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToPasscodeConfirmationRecovery) Execute(c flowpilot.ExecutionContext) error {\n\tif err := c.Stash().Set(shared.StashPathLoginMethod, \"password\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to set login_method to stash: %w\", err)\n\t}\n\n\tif len(c.Stash().Get(shared.StashPathUserID).String()) > 0 {\n\t\tif err := c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateRecovery); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t}\n\t} else {\n\t\tif err := c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailLoginAttempted); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t}\n\t}\n\n\t_ = c.Stash().Set(shared.StashPathPasswordRecoveryPending, true)\n\n\treturn c.Continue(shared.StatePasscodeConfirmation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_to_password_login.go",
    "content": "package credential_usage\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToPasswordLogin struct {\n\tshared.Action\n}\n\nfunc (a ContinueToPasswordLogin) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToPasswordLogin\n}\n\nfunc (a ContinueToPasswordLogin) GetDescription() string {\n\treturn \"Continue to the password login.\"\n}\n\nfunc (a ContinueToPasswordLogin) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\tif deps.Cfg.Privacy.OnlyShowActualLoginMethods && (!c.Stash().Get(shared.StashPathUserHasPassword).Bool() || !deps.Cfg.Password.Enabled) {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToPasswordLogin) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue(shared.StateLoginPassword)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_with_login_identifier.go",
    "content": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype ContinueWithLoginIdentifier struct {\n\tshared.Action\n}\n\nfunc (a ContinueWithLoginIdentifier) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueWithLoginIdentifier\n}\n\nfunc (a ContinueWithLoginIdentifier) GetDescription() string {\n\treturn \"Enter an identifier to login.\"\n}\n\nfunc (a ContinueWithLoginIdentifier) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\temailEnabled := deps.Cfg.Email.Enabled && deps.Cfg.Email.UseAsLoginIdentifier\n\tusernameEnabled := deps.Cfg.Username.Enabled && deps.Cfg.Username.UseAsLoginIdentifier\n\n\tvar input flowpilot.Input\n\tif usernameEnabled && emailEnabled {\n\t\tinput = flowpilot.StringInput(\"identifier\").\n\t\t\tMaxLength(255)\n\t} else if emailEnabled {\n\t\tinput = flowpilot.EmailInput(\"email\").\n\t\t\tMaxLength(deps.Cfg.Email.MaxLength).\n\t\t\tMinLength(3)\n\t} else if usernameEnabled {\n\t\tinput = flowpilot.StringInput(\"username\").\n\t\t\tMaxLength(deps.Cfg.Username.MaxLength).\n\t\t\tMinLength(deps.Cfg.Username.MinLength)\n\t}\n\n\tif input != nil {\n\t\tc.AddInputs(input.\n\t\t\tRequired(true).\n\t\t\tTrimSpace(true).\n\t\t\tLowerCase(true))\n\t}\n\n\tif !deps.Cfg.Password.Enabled &&\n\t\t!deps.Cfg.Email.UseForAuthentication &&\n\t\t!(emailEnabled && deps.Cfg.Saml.Enabled && len(deps.SamlService.Providers()) > 0) {\n\t\tc.SuspendAction()\n\t}\n\n\tif !emailEnabled && !usernameEnabled {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueWithLoginIdentifier) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tidentifierInputName, identifierInputValue, treatIdentifierAsEmail := a.analyzeIdentifierInputs(c)\n\n\tif err := c.Stash().Set(shared.StashPathUserIdentification, identifierInputValue); err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_identification to stash: %w\", err)\n\t}\n\n\tif len(identifierInputValue) == 0 {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tvar userModel *models.User\n\n\tif treatIdentifierAsEmail {\n\t\t// User has submitted an email address.\n\n\t\tif deps.Cfg.Saml.Enabled {\n\t\t\tdomain := strings.Split(identifierInputValue, \"@\")[1]\n\t\t\tif provider, err := deps.SamlService.GetProviderByDomain(domain); err == nil && provider != nil {\n\t\t\t\tauthUrl, err := deps.SamlService.GetAuthUrl(provider, deps.Cfg.Saml.DefaultRedirectUrl, true)\n\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to get auth url: %w\", err)\n\t\t\t\t}\n\n\t\t\t\t_ = c.Payload().Set(\"redirect_url\", authUrl)\n\n\t\t\t\treturn c.Continue(shared.StateThirdParty)\n\t\t\t}\n\t\t}\n\n\t\tvar err error\n\n\t\tuserModel, err = deps.Persister.GetUserPersisterWithConnection(deps.Tx).GetByEmailAddress(identifierInputValue)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// When privacy setting is off return an error when email address does not exist\n\t\tif userModel == nil && deps.Cfg.Privacy.ShowAccountExistenceHints {\n\t\t\tflowInputError := shared.ErrorUnknownEmail\n\t\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\t\tdeps.Tx,\n\t\t\t\tdeps.HttpContext,\n\t\t\t\tmodels.AuditLogLoginFailure,\n\t\t\t\tnil,\n\t\t\t\tflowInputError,\n\t\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t\t}\n\n\t\t\tc.Input().SetError(identifierInputName, flowInputError)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t}\n\n\t\tif err = c.Stash().Set(shared.StashPathEmail, identifierInputValue); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set email to stash: %w\", err)\n\t\t}\n\n\t\tif userModel != nil {\n\t\t\temailModel := userModel.GetEmailByAddress(identifierInputValue)\n\n\t\t\tif emailModel != nil && emailModel.UserID != nil {\n\t\t\t\terr = c.Stash().Set(shared.StashPathUserID, emailModel.UserID.String())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to set user_id to the stash: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// User has submitted a username.\n\t\tvar err error\n\n\t\tuserModel, err = deps.Persister.GetUserPersisterWithConnection(deps.Tx).GetByUsername(identifierInputValue)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get user by username from db: %w\", err)\n\t\t}\n\n\t\tif userModel == nil {\n\t\t\tflowInputError := shared.ErrorUnknownUsername\n\t\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\t\tdeps.Tx,\n\t\t\t\tdeps.HttpContext,\n\t\t\t\tmodels.AuditLogLoginFailure,\n\t\t\t\tnil,\n\t\t\t\tflowInputError,\n\t\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t\t}\n\n\t\t\tc.Input().SetError(identifierInputName, flowInputError)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t}\n\n\t\tif err = c.Stash().Set(shared.StashPathUsername, identifierInputValue); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set username to stash: %w\", err)\n\t\t}\n\n\t\terr = c.Stash().Set(shared.StashPathUserID, userModel.ID.String())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set user_id to the stash: %w\", err)\n\t\t}\n\t\tif primaryEmailModel := userModel.Emails.GetPrimary(); primaryEmailModel != nil {\n\t\t\tif err = c.Stash().Set(shared.StashPathEmail, primaryEmailModel.Address); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set email to stash: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif userModel != nil {\n\t\t_ = c.Stash().Set(shared.StashPathUserHasPassword, userModel.PasswordCredential != nil)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasPasskey, len(userModel.GetPasskeys()) > 0)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasWebauthnCredential, len(userModel.WebauthnCredentials) > 0)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasUsername, userModel.GetUsername() != nil)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasEmails, len(userModel.Emails) > 0)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasOTPSecret, userModel.OTPSecret != nil)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasSecurityKey, len(userModel.GetSecurityKeys()) > 0)\n\t}\n\n\tif !treatIdentifierAsEmail && userModel != nil && !deps.Cfg.Password.Enabled && userModel.Emails.GetPrimary() == nil {\n\t\t// The user has entered a username of an existing user, but passwords are disabled, and the user does not have\n\t\t// an email address to send the passcode.\n\t\treturn c.Error(flowpilot.ErrorFlowDiscontinuity.Wrap(errors.New(\"user has no email address and passwords are disabled\")))\n\t}\n\n\tif deps.Cfg.Privacy.OnlyShowActualLoginMethods {\n\t\temailAvailable := deps.Cfg.Email.UseForAuthentication && userModel != nil && userModel.Emails.GetPrimary() != nil\n\t\tpasswordAvailable := deps.Cfg.Password.Enabled && userModel != nil && userModel.PasswordCredential != nil\n\t\tpasskeysAvailable := deps.Cfg.Passkey.Enabled && userModel != nil && len(userModel.GetPasskeys()) > 0\n\t\tavailableMethods := 0\n\t\tif emailAvailable {\n\t\t\tavailableMethods += 1\n\t\t}\n\t\tif passwordAvailable {\n\t\t\tavailableMethods += 1\n\t\t}\n\t\tif passkeysAvailable {\n\t\t\tavailableMethods += 1\n\t\t}\n\n\t\tswitch {\n\t\tcase availableMethods > 1:\n\t\t\treturn c.Continue(shared.StateLoginMethodChooser)\n\t\tcase emailAvailable:\n\t\t\treturn a.continueToPasscodeConfirmation(c)\n\t\tcase passwordAvailable:\n\t\t\treturn c.Continue(shared.StateLoginPassword)\n\t\tcase passkeysAvailable:\n\t\t\t//goland:noinspection GoDfaNilDereference\n\t\t\tuserModel.WebauthnCredentials = userModel.GetPasskeys()\n\t\t\tparams := services.GenerateRequestOptionsPasskeyParams{Tx: deps.Tx, User: userModel}\n\n\t\t\tsessionDataModel, requestOptions, err := deps.WebauthnService.GenerateRequestOptionsPasskey(params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to generate webauthn request options: %w\", err)\n\t\t\t}\n\n\t\t\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID.String())\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to stash webauthn_session_data_id: %w\", err)\n\t\t\t}\n\n\t\t\terr = c.Payload().Set(\"request_options\", requestOptions)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set request_options payload: %w\", err)\n\t\t\t}\n\n\t\t\treturn c.Continue(shared.StateLoginPasskey)\n\t\t}\n\t} else {\n\t\tif deps.Cfg.Email.UseForAuthentication && deps.Cfg.Password.Enabled {\n\t\t\t// Both passcode and password authentication are enabled.\n\t\t\tif treatIdentifierAsEmail || (!treatIdentifierAsEmail && userModel != nil && userModel.Emails.GetPrimary() != nil) {\n\t\t\t\t// The user has entered either an email address, or a username for an existing user who has an email address.\n\t\t\t\treturn c.Continue(shared.StateLoginMethodChooser)\n\t\t\t}\n\n\t\t\t// Either no email was entered or the username does not correspond to an email, passwords are enabled.\n\t\t\treturn c.Continue(shared.StateLoginPassword)\n\t\t}\n\n\t\tif deps.Cfg.Email.UseForAuthentication {\n\t\t\t// Only passcode authentication is enabled; the user must use a passcode.\n\t\t\treturn a.continueToPasscodeConfirmation(c)\n\t\t}\n\n\t\tif deps.Cfg.Password.Enabled {\n\t\t\t// Only password authentication is enabled; the user must use a password.\n\t\t\treturn c.Continue(shared.StateLoginPassword)\n\t\t}\n\t}\n\treturn c.Error(flowpilot.ErrorFlowDiscontinuity.Wrap(errors.New(\"no authentication method enabled\")))\n}\n\n// analyzeIdentifierInputs determines if an input value has been provided for 'identifier', 'email', or 'username',\n// according to the configuration. Also adds an input error to the expected input field, if the value is missing.\n// Returns the related input field name, the provided value, and a flag, indicating if the value should be treated as\n// an email (and not as a username).\nfunc (a ContinueWithLoginIdentifier) analyzeIdentifierInputs(c flowpilot.ExecutionContext) (name, value string, treatAsEmail bool) {\n\tdeps := a.GetDeps(c)\n\temailPattern := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$`)\n\temailEnabled := deps.Cfg.Email.Enabled && deps.Cfg.Email.UseAsLoginIdentifier\n\tusernameEnabled := deps.Cfg.Username.Enabled && deps.Cfg.Username.UseAsLoginIdentifier\n\n\tif emailEnabled && usernameEnabled {\n\t\t// analyze the 'identifier' input field\n\t\tname = \"identifier\"\n\t\tvalue = c.Input().Get(name).String()\n\t\ttreatAsEmail = emailPattern.MatchString(value)\n\t} else if emailEnabled {\n\t\t// analyze the 'email' input field\n\t\tname = \"email\"\n\t\tvalue = c.Input().Get(name).String()\n\t\ttreatAsEmail = true\n\t} else if usernameEnabled {\n\t\t// analyze the 'username' input field\n\t\tname = \"username\"\n\t\tvalue = c.Input().Get(name).String()\n\t\ttreatAsEmail = false\n\t}\n\n\t// If no value could not be determined, set an error for the missing input\n\tif len(value) == 0 && len(name) > 0 {\n\t\tc.Input().SetError(name, flowpilot.ErrorValueMissing)\n\t}\n\n\treturn name, value, treatAsEmail\n}\n\nfunc (a ContinueWithLoginIdentifier) continueToPasscodeConfirmation(c flowpilot.ExecutionContext) error {\n\t// Set the login method for audit logging purposes.\n\tif err := c.Stash().Set(shared.StashPathLoginMethod, \"passcode\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to set login_method to stash: %w\", err)\n\t}\n\n\tif c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\tif err := c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateLogin); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t}\n\t} else {\n\t\tif err := c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailLoginAttempted); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t}\n\t}\n\n\treturn c.Continue(shared.StatePasscodeConfirmation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_password_login.go",
    "content": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n)\n\ntype PasswordLogin struct {\n\tshared.Action\n}\n\nfunc (a PasswordLogin) GetName() flowpilot.ActionName {\n\treturn shared.ActionPasswordLogin\n}\n\nfunc (a PasswordLogin) GetDescription() string {\n\treturn \"Login with a password.\"\n}\n\nfunc (a PasswordLogin) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tc.AddInputs(flowpilot.PasswordInput(\"password\").Required(true))\n\n\tif !deps.Cfg.Password.Enabled {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a PasswordLogin) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif deps.Cfg.RateLimiter.Enabled {\n\t\trateLimitKey := rate_limiter.CreateRateLimitPasswordKey(deps.HttpContext.RealIP(), c.Stash().Get(shared.StashPathUserIdentification).String())\n\t\tretryAfterSeconds, ok, err := rate_limiter.Limit2(deps.PasswordRateLimiter, rateLimitKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rate limiter failed: %w\", err)\n\t\t}\n\n\t\tif !ok {\n\t\t\terr = c.Payload().Set(\"retry_after\", retryAfterSeconds)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set a value for retry_after to the payload: %w\", err)\n\t\t\t}\n\t\t\treturn c.Error(shared.ErrorRateLimitExceeded.Wrap(fmt.Errorf(\"rate limit exceeded for: %s\", rateLimitKey)))\n\t\t}\n\t}\n\n\tvar userID uuid.UUID\n\n\tif c.Stash().Get(shared.StashPathEmail).Exists() {\n\t\temailModel, err := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).FindByAddress(c.Stash().Get(shared.StashPathEmail).String())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to find user by email: %w\", err)\n\t\t}\n\n\t\tif emailModel == nil {\n\t\t\treturn a.wrongCredentialsError(c)\n\t\t}\n\n\t\tuserID = *emailModel.UserID\n\t} else if c.Stash().Get(shared.StashPathUsername).Exists() {\n\t\tusername := c.Stash().Get(shared.StashPathUsername).String()\n\t\tuserModel, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).GetByUsername(username)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to find user via username: %w\", err)\n\t\t}\n\n\t\tif userModel == nil {\n\t\t\treturn a.wrongCredentialsError(c)\n\t\t}\n\n\t\tuserID = userModel.ID\n\t} else {\n\t\treturn a.wrongCredentialsError(c)\n\t}\n\n\terr := deps.PasswordService.VerifyPassword(deps.Tx, userID, c.Input().Get(\"password\").String())\n\tif err != nil {\n\t\tif errors.Is(err, services.ErrorPasswordInvalid) {\n\t\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\t\tdeps.Tx,\n\t\t\t\tdeps.HttpContext,\n\t\t\t\tmodels.AuditLogLoginFailure,\n\t\t\t\t&models.User{ID: userID},\n\t\t\t\terr,\n\t\t\t\tauditlog.Detail(\"login_method\", \"password\"),\n\t\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t\t}\n\n\t\t\treturn a.wrongCredentialsError(c)\n\t\t}\n\n\t\treturn fmt.Errorf(\"failed to verify password: %w\", err)\n\t}\n\n\t// Set only for audit logging purposes.\n\terr = c.Stash().Set(shared.StashPathLoginMethod, \"password\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set login_method to the stash: %w\", err)\n\t}\n\n\tc.PreventRevert()\n\n\terr = c.ExecuteHook(shared.ScheduleMFACreationStates{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n\nfunc (a PasswordLogin) wrongCredentialsError(c flowpilot.ExecutionContext) error {\n\tc.Input().SetError(\"password\", flowpilot.ErrorValueInvalid)\n\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(errors.New(\"wrong credentials\")))\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_password_recovery.go",
    "content": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype PasswordRecovery struct {\n\tshared.Action\n}\n\nfunc (a PasswordRecovery) GetName() flowpilot.ActionName {\n\treturn shared.ActionPasswordRecovery\n}\n\nfunc (a PasswordRecovery) GetDescription() string {\n\treturn \"Submit a new password.\"\n}\n\nfunc (a PasswordRecovery) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tc.AddInputs(flowpilot.PasswordInput(\"new_password\").\n\t\tRequired(true).\n\t\tMinLength(deps.Cfg.Password.MinLength).\n\t\tMaxLength(72),\n\t)\n\n\tif !deps.Cfg.Password.Enabled {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a PasswordRecovery) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tnewPassword := c.Input().Get(\"new_password\").String()\n\n\tif !c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted.Wrap(errors.New(\"user_id does not exist\")))\n\t}\n\n\tauthUserID := c.Stash().Get(shared.StashPathUserID).String()\n\n\terr := deps.PasswordService.RecoverPassword(deps.Tx, uuid.FromStringOrNil(authUserID), newPassword)\n\n\tif err != nil {\n\t\tif errors.Is(err, services.ErrorPasswordInvalid) {\n\t\t\tc.Input().SetError(\"password\", flowpilot.ErrorValueInvalid)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(err))\n\t\t}\n\n\t\treturn fmt.Errorf(\"could not recover password: %w\", err)\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogPasswordChanged,\n\t\t&models.User{ID: uuid.FromStringOrNil(authUserID)},\n\t\tnil,\n\t\tauditlog.Detail(\"context\", \"recovery\"),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserHasPassword, true)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_has_password to the stash: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathPasswordRecoveryPending, false)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set pw_recovery_pending to the stash: %w\", err)\n\t}\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserPasswordChange, uuid.FromStringOrNil(authUserID))\n\n\terr = c.ExecuteHook(shared.ScheduleMFACreationStates{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_remember_me.go",
    "content": "package credential_usage\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype RememberMe struct {\n\tshared.Action\n}\n\nfunc (a RememberMe) GetName() flowpilot.ActionName {\n\treturn shared.ActionRememberMe\n}\n\nfunc (a RememberMe) GetDescription() string {\n\treturn \"Enables the user to stay signed in.\"\n}\n\nfunc (a RememberMe) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tc.AddInputs(flowpilot.BooleanInput(\"remember_me\").Required(true))\n\n\tif deps.Cfg.Session.Cookie.Retention != \"prompt\" {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a RememberMe) Execute(c flowpilot.ExecutionContext) error {\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\trememberMeSelected := c.Input().Get(\"remember_me\").Bool()\n\n\tif err := c.Stash().Set(shared.StashPathRememberMeSelected, rememberMeSelected); err != nil {\n\t\treturn fmt.Errorf(\"failed to set remember_me_selected to stash: %w\", err)\n\t}\n\n\treturn c.Continue(c.GetCurrentState())\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_resend_passcode.go",
    "content": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype ReSendPasscode struct {\n\tshared.Action\n}\n\nfunc (a ReSendPasscode) GetName() flowpilot.ActionName {\n\treturn shared.ActionResendPasscode\n}\n\nfunc (a ReSendPasscode) GetDescription() string {\n\treturn \"Send the passcode email again.\"\n}\n\nfunc (a ReSendPasscode) Initialize(_ flowpilot.InitializationContext) {}\n\nfunc (a ReSendPasscode) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif !c.Stash().Get(shared.StashPathEmail).Exists() {\n\t\treturn errors.New(\"email has not been stashed\")\n\t}\n\n\tif !c.Stash().Get(shared.StashPathPasscodeTemplate).Exists() {\n\t\treturn errors.New(\"passcode_template has not been stashed\")\n\t}\n\n\tif deps.Cfg.RateLimiter.Enabled {\n\t\trateLimitKey := rate_limiter.CreateRateLimitPasscodeKey(deps.HttpContext.RealIP(), c.Stash().Get(shared.StashPathEmail).String())\n\t\tresendAfterSeconds, ok, err := rate_limiter.Limit2(deps.PasscodeRateLimiter, rateLimitKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rate limiter failed: %w\", err)\n\t\t}\n\n\t\tif !ok {\n\t\t\terr = c.Payload().Set(\"resend_after\", resendAfterSeconds)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set a value for resend_after to the payload: %w\", err)\n\t\t\t}\n\t\t\treturn c.Error(shared.ErrorRateLimitExceeded.Wrap(fmt.Errorf(\"rate limit exceeded for: %s\", rateLimitKey)))\n\t\t}\n\t}\n\n\tpasscodeTemplate := c.Stash().Get(shared.StashPathPasscodeTemplate).String()\n\n\tsendParams := services.SendPasscodeParams{\n\t\tTemplate:     passcodeTemplate,\n\t\tEmailAddress: c.Stash().Get(shared.StashPathEmail).String(),\n\t\tLanguage:     deps.HttpContext.Request().Header.Get(\"X-Language\"),\n\t}\n\tpasscodeResult, err := deps.PasscodeService.SendPasscode(deps.Tx, sendParams)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"passcode service failed: %w\", err)\n\t}\n\n\twebhookData := webhook.EmailSend{\n\t\tSubject:          passcodeResult.Subject,\n\t\tBodyPlain:        passcodeResult.BodyPlain,\n\t\tBody:             passcodeResult.BodyHTML,\n\t\tToEmailAddress:   sendParams.EmailAddress,\n\t\tDeliveredByHanko: deps.Cfg.EmailDelivery.Enabled,\n\t\tAcceptLanguage:   sendParams.Language,\n\t\tLanguage:         sendParams.Language,\n\t\tType:             passcodeTemplate,\n\t}\n\n\tif slices.Contains(\n\t\t[]string{\n\t\t\tshared.PasscodeTemplateEmailRegistrationAttempted,\n\t\t\tshared.PasscodeTemplateEmailLoginAttempted,\n\t\t}, passcodeTemplate) {\n\t\twebhookData.Data = webhook.PasscodeData{\n\t\t\tServiceName: deps.Cfg.Service.Name,\n\t\t}\n\t} else {\n\t\twebhookData.Data = webhook.PasscodeData{\n\t\t\tServiceName: deps.Cfg.Service.Name,\n\t\t\tOtpCode:     passcodeResult.Code,\n\t\t\tTTL:         deps.Cfg.Email.PasscodeTtl,\n\t\t\tValidUntil:  passcodeResult.PasscodeModel.CreatedAt.Add(time.Duration(deps.Cfg.Email.PasscodeTtl) * time.Second).UTC().Unix(),\n\t\t}\n\t}\n\n\terr = utils.TriggerWebhooks(deps.HttpContext, deps.Tx, events.EmailSend, webhookData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to trigger webhook: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathPasscodeID, passcodeResult.PasscodeModel.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set passcode_id to stash: %w\", err)\n\t}\n\n\treturn c.Continue(c.GetCurrentState())\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_verify_passcode.go",
    "content": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype VerifyPasscode struct {\n\tshared.Action\n}\n\nfunc (a VerifyPasscode) GetName() flowpilot.ActionName {\n\treturn shared.ActionVerifyPasscode\n}\n\nfunc (a VerifyPasscode) GetDescription() string {\n\treturn \"Enter a passcode.\"\n}\n\nfunc (a VerifyPasscode) Initialize(c flowpilot.InitializationContext) {\n\tc.AddInputs(flowpilot.StringInput(\"code\").Required(true))\n}\n\nfunc (a VerifyPasscode) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif !c.Stash().Get(shared.StashPathPasscodeID).Exists() {\n\t\treturn errors.New(\"passcode_id does not exist in the stash\")\n\t}\n\n\tpasscodeID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathPasscodeID).String())\n\terr := deps.PasscodeService.VerifyPasscodeCode(deps.Tx, passcodeID, c.Input().Get(\"code\").String())\n\tif err != nil {\n\t\tif errors.Is(err, services.ErrorPasscodeInvalid) ||\n\t\t\terrors.Is(err, services.ErrorPasscodeNotFound) ||\n\t\t\terrors.Is(err, services.ErrorPasscodeExpired) {\n\n\t\t\tif c.Stash().Get(shared.StashPathLoginMethod).Exists() {\n\t\t\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\t\t\tdeps.Tx,\n\t\t\t\t\tdeps.HttpContext,\n\t\t\t\t\tmodels.AuditLogLoginFailure,\n\t\t\t\t\t&models.User{ID: uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())},\n\t\t\t\t\terr,\n\t\t\t\t\tauditlog.Detail(\"login_method\", \"passcode\"),\n\t\t\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tc.Input().SetError(\"code\", shared.ErrorPasscodeInvalid)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t}\n\n\t\tif errors.Is(err, services.ErrorPasscodeMaxAttemptsReached) {\n\t\t\tif c.Stash().Get(shared.StashPathLoginMethod).Exists() {\n\t\t\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\t\t\tdeps.Tx,\n\t\t\t\t\tdeps.HttpContext,\n\t\t\t\t\tmodels.AuditLogLoginFailure,\n\t\t\t\t\t&models.User{ID: uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())},\n\t\t\t\t\terr,\n\t\t\t\t\tauditlog.Detail(\"login_method\", \"passcode\"),\n\t\t\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn c.Error(shared.ErrorPasscodeMaxAttemptsReached)\n\t\t}\n\n\t\treturn fmt.Errorf(\"failed to verify passcode: %w\", err)\n\t}\n\n\terr = c.Stash().Delete(\"passcode_id\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete passcode_id from stash: %w\", err)\n\t}\n\n\terr = c.Stash().Delete(\"passcode_email\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete passcode_email from stash: %w\", err)\n\t}\n\n\tif !c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted.Wrap(errors.New(\"account does not exist\")))\n\t}\n\n\terr = c.Stash().Set(shared.StashPathEmailVerified, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserHasEmails, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Registration: record that passcode was USED (not derived from login_method).\n\tif c.IsFlow(shared.FlowRegistration) {\n\t\tif err = c.Stash().Set(shared.StashPathRegistrationAMRUsedPasscode, true); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set %s to the stash: %w\", shared.StashPathRegistrationAMRUsedPasscode, err)\n\t\t}\n\t}\n\n\tc.PreventRevert()\n\n\tif err = c.ExecuteHook(shared.ScheduleMFACreationStates{}); err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_webauthn_generate_request_options.go",
    "content": "package credential_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype WebauthnGenerateRequestOptions struct {\n\tshared.Action\n}\n\nfunc (a WebauthnGenerateRequestOptions) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnGenerateRequestOptions\n}\n\nfunc (a WebauthnGenerateRequestOptions) GetDescription() string {\n\treturn \"Get webauthn request options in order to sign in with a webauthn credential.\"\n}\n\nfunc (a WebauthnGenerateRequestOptions) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() || !deps.Cfg.Passkey.Enabled ||\n\t\t(c.Stash().Get(shared.StashPathUserID).Exists() && !c.Stash().Get(shared.StashPathUserHasPasskey).Bool() && c.GetCurrentState() == shared.StateLoginMethodChooser && deps.Cfg.Privacy.OnlyShowActualLoginMethods) {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a WebauthnGenerateRequestOptions) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tparams := services.GenerateRequestOptionsPasskeyParams{Tx: deps.Tx, User: nil}\n\n\tuserIdStash := c.Stash().Get(shared.StashPathUserID)\n\tif userIdStash.Exists() && deps.Cfg.Privacy.OnlyShowActualLoginMethods {\n\t\tuserId := uuid.FromStringOrNil(userIdStash.String())\n\t\tuserModel, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Get(userId)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif userModel != nil {\n\t\t\t// Filter webauthn credentials and only use passkeys and not security keys, as only passkeys are allowed\n\t\t\tuserModel.WebauthnCredentials = userModel.GetPasskeys()\n\t\t\tparams.User = userModel\n\t\t}\n\t}\n\n\tsessionDataModel, requestOptions, err := deps.WebauthnService.GenerateRequestOptionsPasskey(params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate webauthn request options: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash webauthn_session_data_id: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserID, sessionDataModel.UserId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash user_id: %w\", err)\n\t}\n\n\terr = c.Payload().Set(\"request_options\", requestOptions)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set request_options payload: %w\", err)\n\t}\n\n\treturn c.Continue(shared.StateLoginPasskey)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_webauthn_verify_assertion_response.go",
    "content": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnVerifyAssertionResponse struct {\n\tshared.Action\n}\n\nfunc (a WebauthnVerifyAssertionResponse) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnVerifyAssertionResponse\n}\n\nfunc (a WebauthnVerifyAssertionResponse) GetDescription() string {\n\treturn \"Send the result which was generated by using a webauthn credential.\"\n}\n\nfunc (a WebauthnVerifyAssertionResponse) Initialize(c flowpilot.InitializationContext) {\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\tc.SuspendAction()\n\t}\n\n\tc.AddInputs(flowpilot.JSONInput(\"assertion_response\").Required(true))\n}\n\nfunc (a WebauthnVerifyAssertionResponse) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif !c.Stash().Get(shared.StashPathWebauthnSessionDataID).Exists() {\n\t\treturn errors.New(\"webauthn_session_data_id is not present in the stash\")\n\t}\n\n\tsessionDataID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathWebauthnSessionDataID).String())\n\tassertionResponse := c.Input().Get(\"assertion_response\").String()\n\n\tisMFA := c.Stash().Get(shared.StashPathMFAUsageMethod).String() == \"security_key\"\n\n\tparams := services.VerifyAssertionResponseParams{\n\t\tTx:                deps.Tx,\n\t\tSessionDataID:     sessionDataID,\n\t\tAssertionResponse: assertionResponse,\n\t\tIsMFA:             isMFA,\n\t}\n\n\tuserModel, err := deps.WebauthnService.VerifyAssertionResponse(params)\n\tif err != nil {\n\t\tif errors.Is(err, services.ErrInvalidWebauthnCredential) ||\n\t\t\terrors.Is(err, services.ErrInvalidWebauthnCredentialMFAOnly) {\n\n\t\t\tif errors.Is(err, services.ErrInvalidWebauthnCredentialMFAOnly) {\n\t\t\t\tc.SetFlowError(shared.ErrorWebauthnCredentialInvalidMFAOnly)\n\t\t\t} else {\n\t\t\t\tc.SetFlowError(shared.ErrorPasskeyInvalid.Wrap(err))\n\t\t\t}\n\n\t\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\t\tdeps.Tx,\n\t\t\t\tdeps.HttpContext,\n\t\t\t\tmodels.AuditLogLoginFailure,\n\t\t\t\tuserModel,\n\t\t\t\terr,\n\t\t\t\tauditlog.Detail(\"login_method\", \"passkey\"),\n\t\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t\t}\n\n\t\t\treturn c.Continue(c.GetCurrentState())\n\t\t}\n\n\t\treturn fmt.Errorf(\"failed to verify assertion response: %w\", err)\n\t}\n\n\t// Set only for audit logging purposes.\n\tif !isMFA {\n\t\terr = c.Stash().Set(shared.StashPathLoginMethod, \"passkey\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set login_method to the stash: %w\", err)\n\t\t}\n\t}\n\n\tuserIdStash := c.Stash().Get(shared.StashPathUserID)\n\tif userIdStash.Exists() && userIdStash.String() != uuid.Nil.String() && userIdStash.String() != userModel.ID.String() {\n\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\tdeps.Tx,\n\t\t\tdeps.HttpContext,\n\t\t\tmodels.AuditLogLoginFailure,\n\t\t\tuserModel,\n\t\t\terr,\n\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t}\n\n\t\tc.SetFlowError(shared.ErrorPasskeyInvalid)\n\t\treturn c.Continue(shared.StateError)\n\t}\n\n\tif userModel != nil {\n\t\t_ = c.Stash().Set(shared.StashPathUserID, userModel.ID.String())\n\t\t_ = c.Stash().Set(shared.StashPathUsername, userModel.GetUsername())\n\t\t_ = c.Stash().Set(shared.StashPathUserHasPasskey, len(userModel.GetPasskeys()) > 0)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasPassword, userModel.PasswordCredential != nil)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasWebauthnCredential, len(userModel.WebauthnCredentials) > 0)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasOTPSecret, userModel.OTPSecret != nil)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasUsername, userModel.GetUsername() != nil)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasEmails, len(userModel.Emails) > 0)\n\t\t_ = c.Stash().Set(shared.StashPathUserHasSecurityKey, len(userModel.GetSecurityKeys()) > 0)\n\n\t\tif primary := userModel.Emails.GetPrimary(); primary != nil {\n\t\t\t_ = c.Stash().Set(shared.StashPathEmail, primary.Address)\n\t\t}\n\t}\n\n\terr = c.ExecuteHook(shared.ScheduleMFACreationStates{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/hook_send_passcode.go",
    "content": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype SendPasscode struct {\n\tshared.Action\n}\n\nfunc (h SendPasscode) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif c.GetFlowError() != nil {\n\t\treturn nil\n\t}\n\n\tif !c.Stash().Get(shared.StashPathEmail).Exists() {\n\t\treturn errors.New(\"email has not been stashed\")\n\t}\n\n\tif !c.Stash().Get(shared.StashPathPasscodeTemplate).Exists() {\n\t\treturn errors.New(\"passcode_template has not been stashed\")\n\t}\n\n\tif deps.Cfg.RateLimiter.Enabled {\n\t\trateLimitKey := rate_limiter.CreateRateLimitPasscodeKey(deps.HttpContext.RealIP(), c.Stash().Get(shared.StashPathEmail).String())\n\t\tresendAfterSeconds, ok, err := rate_limiter.Limit2(deps.PasscodeRateLimiter, rateLimitKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rate limiter failed: %w\", err)\n\t\t}\n\n\t\tif !ok {\n\t\t\terr = c.Payload().Set(\"resend_after\", resendAfterSeconds)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set a value for resend_after to the payload: %w\", err)\n\t\t\t}\n\n\t\t\tc.SetFlowError(shared.ErrorRateLimitExceeded.Wrap(fmt.Errorf(\"rate limit exceeded for: %s\", rateLimitKey)))\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tvalidationParams := services.ValidatePasscodeParams{\n\t\tTx:         deps.Tx,\n\t\tPasscodeID: uuid.FromStringOrNil(c.Stash().Get(shared.StashPathPasscodeID).String()),\n\t}\n\n\tpasscodeIsValid, err := deps.PasscodeService.ValidatePasscode(validationParams)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate existing passcode: %w\", err)\n\t}\n\n\tisDifferentEmailAddress := c.Stash().Get(shared.StashPathEmail).String() != c.Stash().Get(shared.StashPathPasscodeEmail).String()\n\n\tpasscodeTemplate := c.Stash().Get(shared.StashPathPasscodeTemplate).String()\n\n\tif !passcodeIsValid || isDifferentEmailAddress {\n\t\tsendParams := services.SendPasscodeParams{\n\t\t\tTemplate:     passcodeTemplate,\n\t\t\tEmailAddress: c.Stash().Get(shared.StashPathEmail).String(),\n\t\t\tLanguage:     deps.HttpContext.Request().Header.Get(\"X-Language\"),\n\t\t}\n\n\t\tpasscodeResult, err := deps.PasscodeService.SendPasscode(deps.Tx, sendParams)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"passcode service failed: %w\", err)\n\t\t}\n\n\t\terr = c.Stash().Set(shared.StashPathPasscodeID, passcodeResult.PasscodeModel.ID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_id to stash: %w\", err)\n\t\t}\n\n\t\terr = c.Stash().Set(shared.StashPathPasscodeEmail, c.Stash().Get(shared.StashPathEmail).String())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_email to stash: %w\", err)\n\t\t}\n\n\t\twebhookData := webhook.EmailSend{\n\t\t\tSubject:          passcodeResult.Subject,\n\t\t\tBody:             passcodeResult.BodyHTML,\n\t\t\tBodyPlain:        passcodeResult.BodyPlain,\n\t\t\tToEmailAddress:   sendParams.EmailAddress,\n\t\t\tDeliveredByHanko: deps.Cfg.EmailDelivery.Enabled,\n\t\t\tAcceptLanguage:   sendParams.Language,\n\t\t\tLanguage:         sendParams.Language,\n\t\t\tType:             passcodeTemplate,\n\t\t}\n\n\t\tif slices.Contains(\n\t\t\t[]string{\n\t\t\t\tshared.PasscodeTemplateEmailRegistrationAttempted,\n\t\t\t\tshared.PasscodeTemplateEmailLoginAttempted,\n\t\t\t}, passcodeTemplate) {\n\t\t\twebhookData.Data = webhook.PasscodeData{\n\t\t\t\tServiceName: deps.Cfg.Service.Name,\n\t\t\t}\n\t\t} else {\n\t\t\twebhookData.Data = webhook.PasscodeData{\n\t\t\t\tServiceName: deps.Cfg.Service.Name,\n\t\t\t\tOtpCode:     passcodeResult.Code,\n\t\t\t\tTTL:         deps.Cfg.Email.PasscodeTtl,\n\t\t\t\tValidUntil:  passcodeResult.PasscodeModel.CreatedAt.Add(time.Duration(deps.Cfg.Email.PasscodeTtl) * time.Second).UTC().Unix(),\n\t\t\t}\n\t\t}\n\n\t\terr = utils.TriggerWebhooks(deps.HttpContext, deps.Tx, events.EmailSend, webhookData)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to trigger webhook: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/device_trust/action_trust_device.go",
    "content": "package device_trust\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype TrustDevice struct {\n\tshared.Action\n}\n\nfunc (a TrustDevice) GetName() flowpilot.ActionName {\n\treturn shared.ActionTrustDevice\n}\n\nfunc (a TrustDevice) GetDescription() string {\n\treturn \"Trust this device, to skip MFA on subsequent logins.\"\n}\n\nfunc (a TrustDevice) Initialize(c flowpilot.InitializationContext) {}\n\nfunc (a TrustDevice) Execute(c flowpilot.ExecutionContext) error {\n\tif err := c.Stash().Set(shared.StashPathDeviceTrustGranted, true); err != nil {\n\t\treturn fmt.Errorf(\"failed to set device_trust_granted to the stash: %w\", err)\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/device_trust/hook_issue_trust_device_cookie.go",
    "content": "package device_trust\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"net/http\"\n)\n\ntype IssueTrustDeviceCookie struct {\n\tshared.Action\n}\n\nfunc (h IssueTrustDeviceCookie) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif deps.Cfg.MFA.DeviceTrustPolicy == \"never\" ||\n\t\t(deps.Cfg.MFA.DeviceTrustPolicy == \"prompt\" && !c.Stash().Get(shared.StashPathDeviceTrustGranted).Bool()) {\n\t\treturn nil\n\t}\n\n\tif !c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\treturn fmt.Errorf(\"user id does not exist in the stash\")\n\t}\n\n\tuserID, err := uuid.FromString(c.Stash().Get(shared.StashPathUserID).String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse stashed user_id into a uuid: %w\", err)\n\t}\n\n\tdeviceTrustService := services.DeviceTrustService{\n\t\tPersister:   deps.Persister.GetTrustedDevicePersisterWithConnection(deps.Tx),\n\t\tCfg:         deps.Cfg,\n\t\tHttpContext: deps.HttpContext,\n\t}\n\n\t// Generate new token for this user\n\tdeviceToken, err := deviceTrustService.GenerateRandomToken(64)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate trusted device token: %w\", err)\n\t}\n\n\tname := deps.Cfg.MFA.DeviceTrustCookieName\n\tmaxAge := int(deps.Cfg.MFA.DeviceTrustDuration.Seconds())\n\n\tif maxAge > 0 {\n\t\terr = deviceTrustService.CreateTrustedDevice(userID, deviceToken)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store trusted device: %w\", err)\n\t\t}\n\t}\n\n\t// Read existing cookie entries for multi-user support\n\tvar entries []services.DeviceTrustEntry\n\texistingCookie, _ := deps.HttpContext.Cookie(name)\n\tif existingCookie != nil {\n\t\tentries = deviceTrustService.ParseDeviceTrustCookie(existingCookie.Value)\n\t}\n\n\t// Remove existing entry for this user (if any)\n\tvar filteredEntries []services.DeviceTrustEntry\n\tfor _, entry := range entries {\n\t\tif entry.UserID.String() != userID.String() {\n\t\t\tfilteredEntries = append(filteredEntries, entry)\n\t\t}\n\t}\n\n\t// Add new entry at the front (most recent)\n\tnewEntry := services.DeviceTrustEntry{\n\t\tUserID:      userID,\n\t\tDeviceToken: deviceToken,\n\t}\n\tentries = append([]services.DeviceTrustEntry{newEntry}, filteredEntries...)\n\n\t// Enforce max users limit\n\tmaxUsers := deps.Cfg.MFA.DeviceTrustMaxUsersPerDevice\n\tif maxUsers <= 0 {\n\t\tmaxUsers = 20 // Default\n\t}\n\tif len(entries) > maxUsers {\n\t\tentries = entries[:maxUsers]\n\t}\n\n\t// Serialize composite cookie value\n\tcookieValue := deviceTrustService.SerializeDeviceTrustCookie(entries)\n\n\tcookie := new(http.Cookie)\n\tcookie.Name = name\n\tcookie.Value = cookieValue\n\tcookie.Path = \"/\"\n\tcookie.HttpOnly = true\n\tcookie.Secure = true\n\tcookie.MaxAge = maxAge\n\tcookie.SameSite = http.SameSiteNoneMode\n\n\tdeps.HttpContext.SetCookie(cookie)\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/device_trust/hook_schedule_trust_device_state.go",
    "content": "package device_trust\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ScheduleTrustDeviceState struct {\n\tshared.Action\n}\n\nfunc (h ScheduleTrustDeviceState) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif !deps.Cfg.MFA.Enabled || deps.Cfg.MFA.DeviceTrustPolicy != \"prompt\" {\n\t\treturn nil\n\t}\n\n\tif c.IsFlow(shared.FlowLogin) && c.Stash().Get(shared.StashPathLoginMethod).String() == \"passkey\" {\n\t\treturn nil\n\t}\n\n\tif !c.Stash().Get(shared.StashPathUserHasSecurityKey).Bool() &&\n\t\t!c.Stash().Get(shared.StashPathUserHasOTPSecret).Bool() {\n\t\treturn nil\n\t}\n\n\tdeviceTrustService := services.DeviceTrustService{\n\t\tPersister:   deps.Persister.GetTrustedDevicePersisterWithConnection(deps.Tx),\n\t\tCfg:         deps.Cfg,\n\t\tHttpContext: deps.HttpContext,\n\t}\n\n\tuserID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())\n\n\tif !deviceTrustService.CheckDeviceTrust(userID) {\n\t\tc.ScheduleStates(shared.StateDeviceTrust)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/flows.go",
    "content": "package flow\n\nimport (\n\t\"time\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/capabilities\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/credential_onboarding\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/credential_usage\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/device_trust\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/login\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/mfa_creation\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/mfa_usage\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/profile\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/registration\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/user_details\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\nvar CapabilitiesSubFlow = flowpilot.NewSubFlow(shared.FlowCapabilities).\n\tState(shared.StatePreflight, capabilities.RegisterClientCapabilities{}).\n\tMustBuild()\n\nvar CredentialUsageSubFlow = flowpilot.NewSubFlow(shared.FlowCredentialUsage).\n\tState(shared.StateLoginInit,\n\t\tcredential_usage.ContinueWithLoginIdentifier{},\n\t\tcredential_usage.WebauthnGenerateRequestOptions{},\n\t\tcredential_usage.WebauthnVerifyAssertionResponse{},\n\t\tcredential_usage.RememberMe{},\n\t\tshared.ThirdPartyOAuth{}).\n\tState(shared.StateLoginPasskey,\n\t\tcredential_usage.WebauthnVerifyAssertionResponse{},\n\t\tshared.Back{}).\n\tState(shared.StateThirdParty,\n\t\tshared.ExchangeToken{},\n\t\tshared.Back{}).\n\tState(shared.StateLoginMethodChooser,\n\t\tcredential_usage.ContinueToPasswordLogin{},\n\t\tcredential_usage.ContinueToPasscodeConfirmation{},\n\t\tcredential_usage.WebauthnGenerateRequestOptions{},\n\t\tshared.Back{},\n\t).\n\tState(shared.StateLoginPassword,\n\t\tcredential_usage.PasswordLogin{},\n\t\tcredential_usage.ContinueToPasscodeConfirmationRecovery{},\n\t\tshared.Back{},\n\t).\n\tState(shared.StateLoginPasswordRecovery,\n\t\tcredential_usage.PasswordRecovery{}).\n\tState(shared.StatePasscodeConfirmation,\n\t\tcredential_usage.VerifyPasscode{},\n\t\tcredential_usage.ReSendPasscode{},\n\t\tshared.Back{}).\n\tBeforeState(shared.StatePasscodeConfirmation,\n\t\tcredential_usage.SendPasscode{}).\n\tMustBuild()\n\nvar CredentialOnboardingSubFlow = flowpilot.NewSubFlow(shared.FlowCredentialOnboarding).\n\tState(shared.StateCredentialOnboardingChooser,\n\t\tcredential_onboarding.ContinueToPasskey{},\n\t\tcredential_onboarding.ContinueToPassword{},\n\t\tcredential_onboarding.SkipCredentialOnboardingMethodChooser{},\n\t\tshared.Back{}).\n\tState(shared.StateOnboardingCreatePasskey,\n\t\tcredential_onboarding.WebauthnGenerateCreationOptions{},\n\t\tcredential_onboarding.SkipPasskey{},\n\t\tshared.Back{}).\n\tState(shared.StateOnboardingVerifyPasskeyAttestation,\n\t\tcredential_onboarding.WebauthnVerifyAttestationResponse{},\n\t\tshared.Back{}).\n\tState(shared.StatePasswordCreation,\n\t\tcredential_onboarding.RegisterPassword{},\n\t\tcredential_onboarding.SkipPassword{},\n\t\tshared.Back{}).\n\tMustBuild()\n\nvar UserDetailsSubFlow = flowpilot.NewSubFlow(shared.FlowUserDetails).\n\tState(shared.StateOnboardingUsername,\n\t\tuser_details.UsernameSet{},\n\t\tuser_details.SkipUsername{}).\n\tState(shared.StateOnboardingEmail,\n\t\tuser_details.EmailAddressSet{},\n\t\tuser_details.SkipEmail{}).\n\tMustBuild()\n\nvar MFACreationSubFlow = flowpilot.NewSubFlow(shared.FlowMFACreation).\n\tState(shared.StateMFAMethodChooser,\n\t\tmfa_creation.ContinueToOTPSecretCreation{},\n\t\tmfa_creation.ContinueToSecurityKeyCreation{},\n\t\tmfa_creation.SkipMFA{},\n\t\tshared.Back{}).\n\tBeforeState(shared.StateMFAOTPSecretCreation,\n\t\tmfa_creation.OTPSecretGenerate{}).\n\tState(shared.StateMFAOTPSecretCreation,\n\t\tmfa_creation.OTPCodeVerify{},\n\t\tshared.Back{}).\n\tState(shared.StateMFASecurityKeyCreation,\n\t\tmfa_creation.WebauthnGenerateCreationOptionsForSecurityKeys{},\n\t\tshared.Back{}).\n\tMustBuild()\n\nvar MFAUsageSubFlow = flowpilot.NewSubFlow(shared.FlowMFAUsage).\n\tState(shared.StateLoginSecurityKey,\n\t\tmfa_usage.WebauthnGenerateRequestOptionsSecurityKey{},\n\t\tmfa_usage.ContinueToLoginOTP{}).\n\tState(shared.StateLoginOTP,\n\t\tmfa_usage.OTPCodeValidate{},\n\t\tmfa_usage.ContinueToLoginSecurityKey{}).\n\tMustBuild()\n\nvar DeviceTrustSubFlow = flowpilot.NewSubFlow(shared.FlowDeviceTrust).\n\tState(shared.StateDeviceTrust,\n\t\tdevice_trust.TrustDevice{},\n\t\tshared.Skip{},\n\t\tshared.Back{}).\n\tMustBuild()\n\nfunc NewLoginFlow(debug bool) flowpilot.Flow {\n\treturn flowpilot.NewFlow(shared.FlowLogin).\n\t\tState(shared.StateSuccess).\n\t\tInitialState(shared.StatePreflight, shared.StateLoginInit).\n\t\tErrorState(shared.StateError).\n\t\tBeforeState(shared.StateLoginInit,\n\t\t\tlogin.WebauthnGenerateRequestOptionsForConditionalUi{}).\n\t\tBeforeState(shared.StateSuccess,\n\t\t\tlogin.TriggerLoginWebhook{},\n\t\t\tdevice_trust.IssueTrustDeviceCookie{},\n\t\t\tshared.IssueSession{},\n\t\t\tshared.DetermineAMRValues{},\n\t\t\tshared.GetUserData{}).\n\t\tAfterState(shared.StateOnboardingVerifyPasskeyAttestation,\n\t\t\tshared.WebauthnCredentialSave{}).\n\t\tAfterState(shared.StatePasscodeConfirmation,\n\t\t\tshared.EmailPersistVerifiedStatus{}).\n\t\tAfterState(shared.StatePasswordCreation,\n\t\t\tshared.PasswordSave{}).\n\t\tAfterState(shared.StateOnboardingEmail, login.CreateEmail{}).\n\t\tAfterState(shared.StatePasscodeConfirmation, login.CreateEmail{}).\n\t\tAfterFlow(shared.FlowCredentialUsage, login.ScheduleOnboardingStates{}).\n\t\tSubFlows(\n\t\t\tCapabilitiesSubFlow,\n\t\t\tCredentialUsageSubFlow,\n\t\t\tCredentialOnboardingSubFlow,\n\t\t\tDeviceTrustSubFlow,\n\t\t\tUserDetailsSubFlow,\n\t\t\tMFACreationSubFlow,\n\t\t\tMFAUsageSubFlow).\n\t\tTTL(24 * time.Hour).\n\t\tDebug(debug).\n\t\tMustBuild()\n}\n\nfunc NewRegistrationFlow(debug bool) flowpilot.Flow {\n\treturn flowpilot.NewFlow(shared.FlowRegistration).\n\t\tState(shared.StateRegistrationInit,\n\t\t\tregistration.RegisterLoginIdentifier{},\n\t\t\tcredential_usage.RememberMe{},\n\t\t\tshared.ThirdPartyOAuth{}).\n\t\tState(shared.StateThirdParty,\n\t\t\tshared.ExchangeToken{},\n\t\t\tshared.Back{}).\n\t\tState(shared.StateSuccess).\n\t\tInitialState(shared.StatePreflight,\n\t\t\tshared.StateRegistrationInit).\n\t\tErrorState(shared.StateError).\n\t\tBeforeState(shared.StateSuccess,\n\t\t\tshared.IssueSession{},\n\t\t\tregistration.DetermineAMRValues{},\n\t\t\tshared.GetUserData{},\n\t\t\tregistration.CreateUser{}).\n\t\tSubFlows(\n\t\t\tCapabilitiesSubFlow,\n\t\t\tCredentialUsageSubFlow,\n\t\t\tCredentialOnboardingSubFlow,\n\t\t\tUserDetailsSubFlow,\n\t\t\tMFACreationSubFlow).\n\t\tTTL(24 * time.Hour).\n\t\tDebug(debug).\n\t\tMustBuild()\n}\n\nfunc NewProfileFlow(debug bool) flowpilot.Flow {\n\treturn flowpilot.NewFlow(shared.FlowProfile).\n\t\tState(shared.StateProfileInit,\n\t\t\tprofile.AccountDelete{},\n\t\t\tprofile.ContinueToOTPSecretCreation{},\n\t\t\tprofile.EmailCreate{},\n\t\t\tprofile.EmailDelete{},\n\t\t\tprofile.EmailSetPrimary{},\n\t\t\tprofile.EmailVerify{},\n\t\t\tprofile.OTPSecretDelete{},\n\t\t\tprofile.PasswordCreate{},\n\t\t\tprofile.PasswordDelete{},\n\t\t\tprofile.PasswordUpdate{},\n\t\t\tprofile.PatchMetadata{},\n\t\t\tprofile.SecurityKeyCreate{},\n\t\t\tprofile.SecurityKeyDelete{},\n\t\t\tprofile.UsernameCreate{},\n\t\t\tprofile.UsernameDelete{},\n\t\t\tprofile.UsernameUpdate{},\n\t\t\tprofile.WebauthnCredentialCreate{},\n\t\t\tprofile.WebauthnCredentialDelete{},\n\t\t\tprofile.SessionDelete{},\n\t\t\tprofile.WebauthnCredentialRename{},\n\t\t\tprofile.ConnectThirdpartyOauthProvider{},\n\t\t\tprofile.DisconnectThirdpartyOauthProvider{},\n\t\t).\n\t\tState(shared.StateProfileWebauthnCredentialVerification,\n\t\t\tprofile.WebauthnVerifyAttestationResponse{},\n\t\t\tshared.Back{}).\n\t\tState(shared.StateProfileAccountDeleted).\n\t\tState(shared.StateThirdParty, profile.ExchangeToken{}, shared.Back{}).\n\t\tInitialState(shared.StatePreflight, shared.StateProfileInit).\n\t\tErrorState(shared.StateError).\n\t\tBeforeEachAction(profile.RefreshSessionUser{}).\n\t\tBeforeState(shared.StateProfileInit, profile.GetProfileData{}, profile.GetSessions{}).\n\t\tAfterState(shared.StateProfileWebauthnCredentialVerification, shared.WebauthnCredentialSave{}).\n\t\tAfterState(shared.StatePasscodeConfirmation, shared.EmailPersistVerifiedStatus{}).\n\t\tSubFlows(\n\t\t\tCapabilitiesSubFlow,\n\t\t\tCredentialOnboardingSubFlow,\n\t\t\tCredentialUsageSubFlow,\n\t\t\tMFACreationSubFlow).\n\t\tTTL(24 * time.Hour).\n\t\tDebug(debug).\n\t\tMustBuild()\n}\n\nfunc NewTokenExchangeFlow(debug bool) flowpilot.Flow {\n\treturn flowpilot.NewFlow(shared.FlowTokenExchange).\n\t\tState(shared.StateThirdParty,\n\t\t\tshared.ExchangeToken{}).\n\t\tState(shared.StateSuccess).\n\t\tBeforeState(shared.StateSuccess,\n\t\t\tshared.IssueSession{},\n\t\t\tshared.DetermineAMRValues{},\n\t\t\tshared.GetUserData{}).\n\t\tSubFlows(\n\t\t\tCredentialUsageSubFlow,\n\t\t\tUserDetailsSubFlow).\n\t\tAfterState(shared.StatePasscodeConfirmation,\n\t\t\tshared.EmailPersistVerifiedStatus{}).\n\t\tInitialState(shared.StateThirdParty).\n\t\tErrorState(shared.StateError).\n\t\tDebug(debug).\n\t\tMustBuild()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/login/hook_create_email.go",
    "content": "package login\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype CreateEmail struct {\n\tshared.Action\n}\n\nfunc (h CreateEmail) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif !c.Stash().Get(shared.StashPathEmail).Exists() || (deps.Cfg.Email.RequireVerification && !c.Stash().Get(shared.StashPathEmailVerified).Bool()) {\n\t\treturn nil\n\t}\n\n\tif !c.Stash().Get(shared.StashPathLoginOnboardingCreateEmail).Bool() {\n\t\treturn nil\n\t}\n\n\tif err := c.Stash().Delete(shared.StashPathLoginOnboardingCreateEmail); err != nil {\n\t\treturn fmt.Errorf(\"failed to delete login_onboarding_create_email from the stash: %w\", err)\n\t}\n\n\tuserID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())\n\temailModel := models.NewEmail(&userID, c.Stash().Get(shared.StashPathEmail).String())\n\n\terr := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).Create(*emailModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create a new email: %w\", err)\n\t}\n\n\tprimaryEmail := models.NewPrimaryEmail(emailModel.ID, userID)\n\terr = deps.Persister.GetPrimaryEmailPersisterWithConnection(deps.Tx).Create(*primaryEmail)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create a new primary email: %w\", err)\n\t}\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserEmailCreate, userID)\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/login/hook_schedule_onboarding_states.go",
    "content": "package login\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/device_trust\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ScheduleOnboardingStates struct {\n\tshared.Action\n}\n\nfunc (h ScheduleOnboardingStates) Execute(c flowpilot.HookExecutionContext) error {\n\tif c.Stash().Get(shared.StashPathLoginOnboardingScheduled).Bool() {\n\t\treturn nil\n\t}\n\n\tif err := c.Stash().Set(shared.StashPathLoginOnboardingScheduled, true); err != nil {\n\t\treturn fmt.Errorf(\"failed to set login_onboarding_scheduled to the stash: %w\", err)\n\t}\n\n\tmfaUsageStates := h.determineMFAUsageStates(c)\n\tuserDetailOnboardingStates := h.determineUserDetailOnboardingStates(c)\n\tcredentialOnboardingStates := h.determineCredentialOnboardingStates(c)\n\n\tc.ScheduleStates(mfaUsageStates...)\n\n\tif c.Stash().Get(shared.StashPathPasswordRecoveryPending).Bool() {\n\t\tc.ScheduleStates(shared.StateLoginPasswordRecovery)\n\t}\n\n\tc.ScheduleStates(userDetailOnboardingStates...)\n\tc.ScheduleStates(credentialOnboardingStates...)\n\n\terr := c.ExecuteHook(device_trust.ScheduleTrustDeviceState{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.ScheduleStates(shared.StateSuccess)\n\treturn nil\n}\n\nfunc (h ScheduleOnboardingStates) determineMFAUsageStates(c flowpilot.HookExecutionContext) []flowpilot.StateName {\n\tdeps := h.GetDeps(c)\n\tcfg := deps.Cfg\n\tresult := make([]flowpilot.StateName, 0)\n\n\tif !cfg.MFA.Enabled {\n\t\treturn result\n\t}\n\n\tif c.Stash().Get(shared.StashPathLoginMethod).String() == \"passkey\" {\n\t\treturn result\n\t}\n\n\tdeviceTrustService := services.DeviceTrustService{\n\t\tPersister:   deps.Persister.GetTrustedDevicePersisterWithConnection(deps.Tx),\n\t\tCfg:         deps.Cfg,\n\t\tHttpContext: deps.HttpContext,\n\t}\n\n\tuserID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())\n\n\tif deviceTrustService.CheckDeviceTrust(userID) {\n\t\t// The device is trusted, so MFA can be skipped.\n\t\treturn result\n\t}\n\n\tuserHasSecurityKey := c.Stash().Get(shared.StashPathUserHasSecurityKey).Bool()\n\tuserHasOTPSecret := c.Stash().Get(shared.StashPathUserHasOTPSecret).Bool()\n\tattachmentSupported := c.Stash().Get(shared.StashPathSecurityKeyAttachmentSupported).Bool()\n\tuserCanUseOTP := cfg.MFA.TOTP.Enabled && userHasOTPSecret\n\n\tif cfg.MFA.SecurityKeys.Enabled && userHasSecurityKey {\n\t\tswitch {\n\t\tcase !attachmentSupported && !userCanUseOTP:\n\t\t\tc.SetFlowError(shared.ErrorPlatformAuthenticatorRequired)\n\t\t\tresult = append(result, shared.StateError)\n\t\tcase attachmentSupported:\n\t\t\tresult = append(result, shared.StateLoginSecurityKey)\n\t\tcase userCanUseOTP:\n\t\t\tresult = append(result, shared.StateLoginOTP)\n\t\t}\n\t} else if userCanUseOTP {\n\t\tresult = append(result, shared.StateLoginOTP)\n\t}\n\n\treturn result\n}\n\nfunc (h ScheduleOnboardingStates) determineCredentialOnboardingStates(c flowpilot.HookExecutionContext) []flowpilot.StateName {\n\tdeps := h.GetDeps(c)\n\tcfg := deps.Cfg\n\tresult := make([]flowpilot.StateName, 0)\n\n\thasPassword := c.Stash().Get(shared.StashPathUserHasPassword).Bool()\n\thasPasskey := c.Stash().Get(shared.StashPathUserHasPasskey).Bool()\n\twebauthnAvailable := c.Stash().Get(shared.StashPathWebauthnAvailable).Bool()\n\tpasskeyEnabled := webauthnAvailable && deps.Cfg.Passkey.Enabled\n\tpasswordEnabled := deps.Cfg.Password.Enabled\n\tpasswordAndPasskeyEnabled := passkeyEnabled && passwordEnabled\n\n\talwaysAcquirePasskey := cfg.Passkey.AcquireOnLogin == \"always\"\n\talwaysAcquirePassword := cfg.Password.AcquireOnLogin == \"always\"\n\tconditionalAcquirePasskey := cfg.Passkey.AcquireOnLogin == \"conditional\"\n\tconditionalAcquirePassword := cfg.Password.AcquireOnLogin == \"conditional\"\n\tneverAcquirePasskey := cfg.Passkey.AcquireOnLogin == \"never\"\n\tneverAcquirePassword := cfg.Password.AcquireOnLogin == \"never\"\n\n\tif c.Stash().Get(shared.StashPathPasswordRecoveryPending).Bool() {\n\t\t// never acquire password, when recovery has been initiated\n\t\tneverAcquirePassword = true\n\t}\n\n\tif passwordAndPasskeyEnabled {\n\t\tif alwaysAcquirePasskey && alwaysAcquirePassword {\n\t\t\tif !hasPasskey && !hasPassword {\n\t\t\t\tif !cfg.Password.Optional && cfg.Passkey.Optional {\n\t\t\t\t\tresult = append(result, shared.StatePasswordCreation, shared.StateOnboardingCreatePasskey)\n\t\t\t\t} else {\n\t\t\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey, shared.StatePasswordCreation)\n\t\t\t\t}\n\t\t\t} else if hasPasskey && !hasPassword {\n\t\t\t\tresult = append(result, shared.StatePasswordCreation)\n\t\t\t} else if !hasPasskey && hasPassword {\n\t\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t\t}\n\t\t} else if alwaysAcquirePasskey && conditionalAcquirePassword {\n\t\t\tif !hasPasskey && !hasPassword {\n\t\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey) // skip should lead to password onboarding\n\t\t\t} else if !hasPasskey && hasPassword {\n\t\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t\t}\n\t\t} else if conditionalAcquirePasskey && alwaysAcquirePassword {\n\t\t\tif !hasPasskey && !hasPassword {\n\t\t\t\tresult = append(result, shared.StatePasswordCreation) // skip should lead to passkey onboarding\n\t\t\t} else if hasPasskey && !hasPassword {\n\t\t\t\tresult = append(result, shared.StatePasswordCreation)\n\t\t\t}\n\t\t} else if conditionalAcquirePasskey && conditionalAcquirePassword {\n\t\t\tif !hasPasskey && !hasPassword {\n\t\t\t\tresult = append(result, shared.StateCredentialOnboardingChooser) // credential_onboarding_chooser can be skipped\n\t\t\t}\n\t\t} else if conditionalAcquirePasskey && neverAcquirePassword {\n\t\t\tif !hasPasskey && !hasPassword {\n\t\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t\t}\n\t\t} else if neverAcquirePasskey && conditionalAcquirePassword {\n\t\t\tif !hasPasskey && !hasPassword {\n\t\t\t\tresult = append(result, shared.StatePasswordCreation)\n\t\t\t}\n\t\t} else if neverAcquirePasskey && alwaysAcquirePassword {\n\t\t\tif !hasPassword {\n\t\t\t\tresult = append(result, shared.StatePasswordCreation)\n\t\t\t}\n\t\t} else if alwaysAcquirePasskey && neverAcquirePassword {\n\t\t\tif !hasPasskey {\n\t\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t\t}\n\t\t}\n\t} else if passkeyEnabled && (alwaysAcquirePasskey || conditionalAcquirePasskey) {\n\t\tif !hasPasskey {\n\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t}\n\t} else if passwordEnabled && (alwaysAcquirePassword || conditionalAcquirePassword) {\n\t\tif !hasPassword {\n\t\t\tresult = append(result, shared.StatePasswordCreation)\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunc (h ScheduleOnboardingStates) determineUserDetailOnboardingStates(c flowpilot.HookExecutionContext) []flowpilot.StateName {\n\tdeps := h.GetDeps(c)\n\tcfg := deps.Cfg\n\tresult := make([]flowpilot.StateName, 0)\n\n\tuserHasUsername := c.Stash().Get(shared.StashPathUserHasUsername).Bool()\n\tuserHasEmail := c.Stash().Get(shared.StashPathUserHasEmails).Bool()\n\tacquireUsername := !userHasUsername && cfg.Username.Enabled && cfg.Username.AcquireOnLogin\n\tacquireEmail := !userHasEmail && cfg.Email.Enabled && cfg.Email.AcquireOnLogin\n\n\tif acquireUsername && acquireEmail {\n\t\tresult = append(result, shared.StateOnboardingUsername, shared.StateOnboardingEmail)\n\t} else if acquireUsername {\n\t\tresult = append(result, shared.StateOnboardingUsername)\n\t} else if acquireEmail {\n\t\tresult = append(result, shared.StateOnboardingEmail)\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "backend/flow_api/flow/login/hook_trigger_login_webhook.go",
    "content": "package login\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype TriggerLoginWebhook struct {\n\tshared.Action\n}\n\nfunc (h TriggerLoginWebhook) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\tuserID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserLogin, userID)\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/login/hook_webauthn_generate_request_options_cond.go",
    "content": "package login\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype WebauthnGenerateRequestOptionsForConditionalUi struct {\n\tshared.Action\n}\n\nfunc (a WebauthnGenerateRequestOptionsForConditionalUi) Execute(c flowpilot.HookExecutionContext) error {\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\treturn nil\n\t}\n\n\tif !c.Stash().Get(shared.StashPathWebauthnConditionalMediationAvailable).Bool() {\n\t\treturn nil\n\t}\n\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Passkey.Enabled {\n\t\treturn nil\n\t}\n\n\tparams := services.GenerateRequestOptionsPasskeyParams{Tx: deps.Tx}\n\n\tsessionDataModel, requestOptions, err := deps.WebauthnService.GenerateRequestOptionsPasskey(params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate webauthn request options: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID.String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash webauthn_session_data_id: %w\", err)\n\t}\n\n\terr = c.Payload().Set(\"request_options\", requestOptions)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set request_options payload: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_continue_to_otp_secret_creation.go",
    "content": "package mfa_creation\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToOTPSecretCreation struct {\n\tshared.Action\n}\n\nfunc (a ContinueToOTPSecretCreation) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToOTPSecretCreation\n}\n\nfunc (a ContinueToOTPSecretCreation) GetDescription() string {\n\treturn \"Create an OTP secret\"\n}\n\nfunc (a ContinueToOTPSecretCreation) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.MFA.TOTP.Enabled {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToOTPSecretCreation) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue(shared.StateMFAOTPSecretCreation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_continue_to_security_key_creation.go",
    "content": "package mfa_creation\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToSecurityKeyCreation struct {\n\tshared.Action\n}\n\nfunc (a ContinueToSecurityKeyCreation) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToSecurityKeyCreation\n}\n\nfunc (a ContinueToSecurityKeyCreation) GetDescription() string {\n\treturn \"Create a security key\"\n}\n\nfunc (a ContinueToSecurityKeyCreation) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\tattachmentSupported := c.Stash().Get(shared.StashPathSecurityKeyAttachmentSupported).Bool()\n\n\tif !deps.Cfg.MFA.Enabled || !deps.Cfg.MFA.SecurityKeys.Enabled || !attachmentSupported {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\twebauthnAvailable := c.Stash().Get(shared.StashPathWebauthnAvailable).Bool()\n\tmustUsePlatformAttachment := deps.Cfg.MFA.SecurityKeys.AuthenticatorAttachment == \"platform\"\n\tplatformAuthenticatorAvailable := c.Stash().Get(shared.StashPathWebauthnPlatformAuthenticatorAvailable).Bool()\n\n\tif !webauthnAvailable || (mustUsePlatformAttachment && !platformAuthenticatorAvailable) {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToSecurityKeyCreation) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue(shared.StateMFASecurityKeyCreation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_otp_code_verify.go",
    "content": "package mfa_creation\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pquerna/otp/totp\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype OTPCodeVerify struct {\n\tshared.Action\n}\n\nfunc (a OTPCodeVerify) GetName() flowpilot.ActionName {\n\treturn shared.ActionOTPCodeVerify\n}\n\nfunc (a OTPCodeVerify) GetDescription() string {\n\treturn \"Verify an OTP code\"\n}\n\nfunc (a OTPCodeVerify) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tc.AddInputs(flowpilot.StringInput(\"otp_code\").Required(true))\n\n\tif !deps.Cfg.MFA.TOTP.Enabled {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a OTPCodeVerify) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tcode := c.Input().Get(\"otp_code\").String()\n\tsecret := c.Stash().Get(shared.StashPathOTPSecret).String()\n\n\tif !totp.Validate(code, secret) {\n\t\tc.Input().SetError(\"otp_code\", shared.ErrorPasscodeInvalid)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\t_ = c.Stash().Set(shared.StashPathUserHasOTPSecret, true)\n\n\tif c.GetFlowName() != shared.FlowRegistration {\n\t\tvar userID uuid.UUID\n\t\tvar userModel *models.User\n\t\tif c.GetFlowName() == shared.FlowLogin {\n\t\t\tuserID = uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())\n\t\t} else if c.GetFlowName() == shared.FlowProfile {\n\t\t\tuser, ok := c.Get(\"session_user\").(*models.User)\n\t\t\tif !ok {\n\t\t\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t\t\t}\n\t\t\tuserModel = user\n\t\t\tuserID = userModel.ID\n\t\t}\n\n\t\totpSecretModel := models.NewOTPSecret(userID, secret)\n\n\t\terr := deps.Persister.GetOTPSecretPersisterWithConnection(deps.Tx).Create(*otpSecretModel)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create OTP secret: %w\", err)\n\t\t}\n\n\t\tif userModel != nil {\n\t\t\t// Send user an email informing of new MFA method\n\t\t\tif deps.Cfg.SecurityNotifications.Notifications.MFACreate.Enabled {\n\t\t\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\t\t\tEmailAddress: userModel.Emails.GetPrimary().Address,\n\t\t\t\t\tTemplate:     \"mfa_create\",\n\t\t\t\t\tHttpContext:  deps.HttpContext,\n\t\t\t\t\tUserContext:  *userModel,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\tdeps.Tx,\n\t\t\tdeps.HttpContext,\n\t\t\tmodels.AuditLogOTPCreated,\n\t\t\t&models.User{ID: userID},\n\t\t\tnil,\n\t\t\tauditlog.Detail(\"otp_secret\", otpSecretModel.ID),\n\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()),\n\t\t)\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t}\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_webauthn_generate_creation_options_for_security_keys.go",
    "content": "package mfa_creation\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype WebauthnGenerateCreationOptionsForSecurityKeys struct {\n\tshared.Action\n}\n\nfunc (a WebauthnGenerateCreationOptionsForSecurityKeys) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnGenerateCreationOptions\n}\n\nfunc (a WebauthnGenerateCreationOptionsForSecurityKeys) GetDescription() string {\n\treturn \"Get WebAuthn creation options to register a WebAuthn credential.\"\n}\n\nfunc (a WebauthnGenerateCreationOptionsForSecurityKeys) Initialize(c flowpilot.InitializationContext) {\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a WebauthnGenerateCreationOptionsForSecurityKeys) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif !c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\treturn errors.New(\"user_id does not exist in the stash\")\n\t}\n\n\tif !c.Stash().Get(shared.StashPathEmail).Exists() && !c.Stash().Get(shared.StashPathUsername).Exists() {\n\t\treturn errors.New(\"either email or username must exist in the stash\")\n\t}\n\n\tuserID, err := uuid.FromString(c.Stash().Get(shared.StashPathUserID).String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse user id as a uuid: %w\", err)\n\t}\n\n\temail := c.Stash().Get(shared.StashPathEmail).String()\n\tusername := c.Stash().Get(shared.StashPathUsername).String()\n\n\tparams := services.GenerateCreationOptionsParams{\n\t\tTx:       deps.Tx,\n\t\tUserID:   userID,\n\t\tEmail:    &email,\n\t\tUsername: &username,\n\t}\n\n\tsessionDataModel, creationOptions, err := deps.WebauthnService.GenerateCreationOptionsSecurityKey(params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate webauthn creation options: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Stash().Set(shared.StashPathCreateMFAOnlyCredential, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Payload().Set(\"creation_options\", creationOptions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue(shared.StateOnboardingVerifyPasskeyAttestation)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/hook_otp_secret_generate.go",
    "content": "package mfa_creation\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/pquerna/otp/totp\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"image/png\"\n)\n\ntype OTPSecretGenerate struct {\n\tshared.Action\n}\n\nfunc (h OTPSecretGenerate) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif c.GetCurrentState() == shared.StateMFAOTPSecretCreation &&\n\t\tc.Stash().Get(shared.StashPathOTPSecret).Exists() &&\n\t\tc.Stash().Get(shared.StashPathOTPImageSource).Exists() {\n\n\t\totpSecret := c.Stash().Get(shared.StashPathOTPSecret).String()\n\t\totpImageSource := c.Stash().Get(shared.StashPathOTPImageSource).String()\n\n\t\t_ = c.Payload().Set(\"otp_secret\", otpSecret)\n\t\t_ = c.Payload().Set(\"otp_image_source\", otpImageSource)\n\n\t\treturn nil\n\t}\n\n\tuserEmail := c.Stash().Get(shared.StashPathEmail).String()\n\tuserUsername := c.Stash().Get(shared.StashPathUsername).String()\n\n\tif userEmail == \"\" && userUsername == \"\" {\n\t\treturn errors.New(\"could not create OTP secret: no email or username found on the stash\")\n\t}\n\n\totpAccountName := userEmail\n\tif userEmail == \"\" {\n\t\totpAccountName = userUsername\n\t}\n\n\totpKey, err := totp.Generate(totp.GenerateOpts{\n\t\tIssuer:      deps.Cfg.Service.Name,\n\t\tAccountName: otpAccountName,\n\t})\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not generate OTP key: %w\", err)\n\t}\n\n\totpImage, err := otpKey.Image(200, 200)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not generate OTP image: %w\", err)\n\t}\n\n\totpImagePNGBuffer := new(bytes.Buffer)\n\terr = png.Encode(otpImagePNGBuffer, otpImage)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not PNG encode OTP image: %w\", err)\n\t}\n\n\totpImageSource := fmt.Sprintf(\n\t\t\"data:image/png;base64,%s\", base64.StdEncoding.EncodeToString(otpImagePNGBuffer.Bytes()))\n\n\totpSecret := otpKey.Secret()\n\n\t_ = c.Stash().Set(shared.StashPathOTPSecret, otpSecret)\n\t_ = c.Stash().Set(shared.StashPathOTPImageSource, otpImageSource)\n\n\t_ = c.Payload().Set(\"otp_secret\", otpSecret)\n\t_ = c.Payload().Set(\"otp_image_source\", otpImageSource)\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/skip_mfa.go",
    "content": "package mfa_creation\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype SkipMFA struct {\n\tshared.Action\n}\n\nfunc (a SkipMFA) GetName() flowpilot.ActionName {\n\treturn shared.ActionSkip\n}\n\nfunc (a SkipMFA) GetDescription() string {\n\treturn \"Skip\"\n}\n\nfunc (a SkipMFA) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.MFA.Optional {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a SkipMFA) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_continue_to_login_otp.go",
    "content": "package mfa_usage\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToLoginOTP struct {\n\tshared.Action\n}\n\nfunc (a ContinueToLoginOTP) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToLoginOTP\n}\n\nfunc (a ContinueToLoginOTP) GetDescription() string {\n\treturn \"Continues the flow to the OTP login.\"\n}\n\nfunc (a ContinueToLoginOTP) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.MFA.TOTP.Enabled || !c.Stash().Get(shared.StashPathUserHasOTPSecret).Bool() {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToLoginOTP) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue(shared.StateLoginOTP)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_continue_to_login_security_key.go",
    "content": "package mfa_usage\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ContinueToLoginSecurityKey struct {\n\tshared.Action\n}\n\nfunc (a ContinueToLoginSecurityKey) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToLoginSecurityKey\n}\n\nfunc (a ContinueToLoginSecurityKey) GetDescription() string {\n\treturn \"Continues the flow to the security key login.\"\n}\n\nfunc (a ContinueToLoginSecurityKey) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\tattachmentSupported := c.Stash().Get(shared.StashPathSecurityKeyAttachmentSupported).Bool()\n\n\tif !deps.Cfg.MFA.SecurityKeys.Enabled || !c.Stash().Get(shared.StashPathUserHasWebauthnCredential).Bool() || !attachmentSupported {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToLoginSecurityKey) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue(shared.StateLoginSecurityKey)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_otp_code_validate.go",
    "content": "package mfa_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pquerna/otp/totp\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n)\n\ntype OTPCodeValidate struct {\n\tshared.Action\n}\n\nfunc (a OTPCodeValidate) GetName() flowpilot.ActionName {\n\treturn shared.ActionOTPCodeValidate\n}\n\nfunc (a OTPCodeValidate) GetDescription() string {\n\treturn \"Validates the provided code.\"\n}\n\nfunc (a OTPCodeValidate) Initialize(c flowpilot.InitializationContext) {\n\tc.AddInputs(flowpilot.StringInput(\"otp_code\").Required(true))\n}\n\nfunc (a OTPCodeValidate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif !c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\treturn errors.New(\"user_id does not exist in the stash\")\n\t}\n\n\tuserID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())\n\n\tif deps.Cfg.RateLimiter.Enabled {\n\t\trateLimitKey := rate_limiter.CreateRateLimitOTPKey(deps.HttpContext.RealIP(), userID.String())\n\t\tretryAfterSeconds, ok, err := rate_limiter.Limit2(deps.OTPRateLimiter, rateLimitKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rate limiter failed: %w\", err)\n\t\t}\n\n\t\tif !ok {\n\t\t\terr = c.Payload().Set(\"retry_after\", retryAfterSeconds)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set a value for retry_after to the payload: %w\", err)\n\t\t\t}\n\t\t\treturn c.Error(shared.ErrorRateLimitExceeded.Wrap(fmt.Errorf(\"rate limit exceeded for: %s\", rateLimitKey)))\n\t\t}\n\t}\n\n\tuserModel, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Get(userID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch user: %w\", err)\n\t}\n\n\tcode := c.Input().Get(\"otp_code\").String()\n\n\tif !totp.Validate(code, userModel.OTPSecret.Secret) {\n\t\tc.Input().SetError(\"otp_code\", shared.ErrorPasscodeInvalid)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathMFAUsageMethod, \"totp\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash mfa_method: %w\", err)\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_webauthn_generate_request_options_security_key.go",
    "content": "package mfa_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype WebauthnGenerateRequestOptionsSecurityKey struct {\n\tshared.Action\n}\n\nfunc (a WebauthnGenerateRequestOptionsSecurityKey) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnGenerateRequestOptions\n}\n\nfunc (a WebauthnGenerateRequestOptionsSecurityKey) GetDescription() string {\n\treturn \"Get webauthn request options in order to sign in with a webauthn credential.\"\n}\n\nfunc (a WebauthnGenerateRequestOptionsSecurityKey) Initialize(c flowpilot.InitializationContext) {\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a WebauthnGenerateRequestOptionsSecurityKey) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tparams := services.GenerateRequestOptionsSecurityKeyParams{\n\t\tTx:     deps.Tx,\n\t\tUserID: uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String()),\n\t}\n\n\tsessionDataModel, requestOptions, err := deps.WebauthnService.GenerateRequestOptionsSecurityKey(params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate webauthn request options: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash webauthn_session_data_id: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathMFAUsageMethod, \"security_key\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash mfa_method: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserID, sessionDataModel.UserId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash user_id: %w\", err)\n\t}\n\n\terr = c.Payload().Set(\"request_options\", requestOptions)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set request_options payload: %w\", err)\n\t}\n\n\treturn c.Continue(shared.StateLoginPasskey)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_account_delete.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype AccountDelete struct {\n\tshared.Action\n}\n\nfunc (a AccountDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionAccountDelete\n}\n\nfunc (a AccountDelete) GetDescription() string {\n\treturn \"Delete an account.\"\n}\n\nfunc (a AccountDelete) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Account.AllowDeletion {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a AccountDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\terr := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Delete(*userModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not delete user: %w\", err)\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogUserDeleted,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tcookie, err := deps.SessionManager.DeleteCookie()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not delete cookie: %w\", err)\n\t}\n\n\tdeps.HttpContext.SetCookie(cookie)\n\n\terr = utils.TriggerWebhooks(deps.HttpContext, deps.Tx, events.UserDelete, admin.FromUserModel(*userModel))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to trrigger webhook: %w\", err)\n\t}\n\n\treturn c.Continue(shared.StateProfileAccountDeleted)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_connect_thirdparty_oauth_provider.go",
    "content": "package profile\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"slices\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n\t\"golang.org/x/oauth2\"\n)\n\ntype ConnectThirdpartyOauthProvider struct {\n\tshared.Action\n}\n\nfunc (a ConnectThirdpartyOauthProvider) GetName() flowpilot.ActionName {\n\treturn shared.ActionConnectThirdpartyOauthProvider\n}\n\nfunc (a ConnectThirdpartyOauthProvider) GetDescription() string {\n\treturn \"Connect a third party provider via OAuth.\"\n}\n\nfunc (a ConnectThirdpartyOauthProvider) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tenabledThirdPartyProviders := deps.Cfg.ThirdParty.Providers.GetEnabled()\n\tenabledCustomThirdPartyProviders := deps.Cfg.ThirdParty.CustomProviders.GetEnabled()\n\n\tif len(enabledCustomThirdPartyProviders) == 0 && len(enabledThirdPartyProviders) == 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tproviderInput := flowpilot.StringInput(\"provider\").\n\t\tHidden(true).\n\t\tRequired(true)\n\n\tavailableProviders := 0\n\tfor _, provider := range enabledThirdPartyProviders {\n\t\t// Check if the user already has an identity with this provider\n\t\t// to avoid duplicates and only show providers that are not yet connected\n\t\tif !slices.ContainsFunc(userModel.Identities, func(identity models.Identity) bool {\n\t\t\treturn identity.ProviderID == provider.ID\n\t\t}) {\n\t\t\tavailableProviders += 1\n\t\t\tproviderInput.AllowedValue(provider.DisplayName, provider.ID)\n\t\t}\n\t}\n\tslices.SortFunc(enabledCustomThirdPartyProviders, func(a, b config.CustomThirdPartyProvider) int {\n\t\treturn cmp.Compare(a.DisplayName, b.DisplayName)\n\t})\n\n\tfor _, provider := range enabledCustomThirdPartyProviders {\n\t\t// Check if the user already has an identity with this provider\n\t\t// to avoid duplicates and only show providers that are not yet connected\n\t\tif !slices.ContainsFunc(userModel.Identities, func(identity models.Identity) bool {\n\t\t\treturn identity.ProviderID == provider.ID\n\t\t}) {\n\t\t\tavailableProviders += 1\n\t\t\tproviderInput.AllowedValue(provider.DisplayName, provider.ID)\n\t\t}\n\t}\n\n\tif availableProviders == 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(\n\t\tflowpilot.StringInput(\"redirect_to\").Hidden(true).Required(true),\n\t\tproviderInput,\n\t\tflowpilot.StringInput(\"code_verifier\").Hidden(true),\n\t)\n}\n\nfunc (a ConnectThirdpartyOauthProvider) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\terrorRedirectTo := deps.HttpContext.Request().Header.Get(\"Referer\")\n\tif errorRedirectTo == \"\" {\n\t\terrorRedirectTo = deps.Cfg.ThirdParty.ErrorRedirectURL\n\t}\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tredirectTo := c.Input().Get(\"redirect_to\").String()\n\tif ok := thirdparty.IsAllowedRedirect(deps.Cfg.ThirdParty, redirectTo); !ok {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tproviderName := c.Input().Get(\"provider\").String()\n\n\tprovider, err := thirdparty.GetProvider(deps.Cfg.ThirdParty, providerName)\n\tif err != nil {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(err))\n\t}\n\n\tcodeVerifier := c.Input().Get(\"code_verifier\")\n\tstate, err := thirdparty.GenerateState(\n\t\t&deps.Cfg,\n\t\tproviderName,\n\t\tredirectTo,\n\t\tthirdparty.GenerateStateForFlowAPI(true),\n\t\tthirdparty.GenerateStateWithPKCECodeVerifier(codeVerifier.String()),\n\t\tthirdparty.GenerateStateWithUserID(userModel.ID),\n\t)\n\tif err != nil {\n\t\treturn c.Error(flowpilot.ErrorTechnical.Wrap(err))\n\t}\n\n\tvar opts []oauth2.AuthCodeOption\n\tif codeVerifier.Exists() {\n\t\topts = append(opts, oauth2.S256ChallengeOption(codeVerifier.String()))\n\t}\n\tauthCodeUrl := provider.AuthCodeURL(string(state), opts...)\n\n\tcookie := &http.Cookie{\n\t\tName:     utils.HankoThirdpartyStateCookie,\n\t\tValue:    string(state),\n\t\tPath:     \"/\",\n\t\tDomain:   deps.Cfg.Session.Cookie.Domain,\n\t\tMaxAge:   300,\n\t\tSecure:   true,\n\t\tHttpOnly: deps.Cfg.Session.Cookie.HttpOnly,\n\t\tSameSite: http.SameSiteNoneMode,\n\t}\n\n\tdeps.HttpContext.SetCookie(cookie)\n\n\tif err = c.Payload().Set(\"redirect_url\", authCodeUrl); err != nil {\n\t\treturn fmt.Errorf(\"failed to set redirect_url to payload: %w\", err)\n\t}\n\n\treturn c.Continue(shared.StateThirdParty)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_continue_to_otp_secret_creation.go",
    "content": "package profile\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype ContinueToOTPSecretCreation struct {\n\tshared.Action\n}\n\nfunc (a ContinueToOTPSecretCreation) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToOTPSecretCreation\n}\n\nfunc (a ContinueToOTPSecretCreation) GetDescription() string {\n\treturn \"Create an OTP secret\"\n}\n\nfunc (a ContinueToOTPSecretCreation) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.MFA.TOTP.Enabled {\n\t\tc.SuspendAction()\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif userModel.OTPSecret != nil {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToOTPSecretCreation) Execute(c flowpilot.ExecutionContext) error {\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tif userModel.Emails != nil {\n\t\t_ = c.Stash().Set(shared.StashPathEmail, userModel.Emails.GetPrimary().Address)\n\t}\n\t_ = c.Stash().Set(shared.StashPathUsername, userModel.Username)\n\n\treturn c.Continue(shared.StateMFAOTPSecretCreation, shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_continue_to_security_key_creation.go",
    "content": "package profile\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype ContinueToSecurityKeyCreation struct {\n\tshared.Action\n}\n\nfunc (a ContinueToSecurityKeyCreation) GetName() flowpilot.ActionName {\n\treturn shared.ActionContinueToSecurityKeyCreation\n}\n\nfunc (a ContinueToSecurityKeyCreation) GetDescription() string {\n\treturn \"Create a security key\"\n}\n\nfunc (a ContinueToSecurityKeyCreation) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.MFA.Enabled || !deps.Cfg.MFA.SecurityKeys.Enabled {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\twebauthnAvailable := c.Stash().Get(shared.StashPathWebauthnAvailable).Bool()\n\tmustUsePlatformAttachment := deps.Cfg.MFA.SecurityKeys.AuthenticatorAttachment == \"platform\"\n\tplatformAuthenticatorAvailable := c.Stash().Get(shared.StashPathWebauthnPlatformAuthenticatorAvailable).Bool()\n\n\tif !webauthnAvailable || (mustUsePlatformAttachment && !platformAuthenticatorAvailable) {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif len(userModel.GetSecurityKeys()) >= deps.Cfg.MFA.SecurityKeys.Limit {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a ContinueToSecurityKeyCreation) Execute(c flowpilot.ExecutionContext) error {\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\t_ = c.Stash().Set(shared.StashPathUserID, userModel.ID)\n\t_ = c.Stash().Set(shared.StashPathEmail, userModel.Emails.GetPrimary().Address)\n\t_ = c.Stash().Set(shared.StashPathUsername, userModel.Username)\n\n\treturn c.Continue(shared.StateMFASecurityKeyCreation, shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_disconnect_thirdparty_oauth_provider.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype DisconnectThirdpartyOauthProvider struct {\n\tshared.Action\n}\n\nfunc (a DisconnectThirdpartyOauthProvider) GetName() flowpilot.ActionName {\n\treturn shared.ActionDisconnectThirdpartyOauthProvider\n}\n\nfunc (a DisconnectThirdpartyOauthProvider) GetDescription() string {\n\treturn \"Disconnect a third party provider via OAuth.\"\n}\n\nfunc (a DisconnectThirdpartyOauthProvider) Initialize(c flowpilot.InitializationContext) {\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif len(userModel.Identities) == 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tinput := flowpilot.StringInput(\"identity_id\").Required(true)\n\n\tfor _, identity := range userModel.Identities {\n\t\tinput.AllowedValue(identity.ProviderID, identity.ID)\n\t}\n}\n\nfunc (a DisconnectThirdpartyOauthProvider) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tidentityIDStr := c.Input().Get(\"identity_id\").String()\n\tidentityID, err := uuid.FromString(identityIDStr)\n\tif err != nil {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(err))\n\t}\n\n\tif !slices.ContainsFunc(userModel.Identities, func(identity models.Identity) bool {\n\t\treturn identity.ID == identityID\n\t}) {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(fmt.Errorf(\"identity not found\")))\n\t}\n\n\tidentityPersister := deps.Persister.GetIdentityPersisterWithConnection(deps.Tx)\n\n\tidentity, err := identityPersister.GetByID(identityID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch identity from db: %w\", err)\n\t}\n\tif identity == nil {\n\t\treturn errors.New(\"identity not found\")\n\t}\n\n\terr = identityPersister.Delete(*identity)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete identity from db: %w\", err)\n\t}\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_create.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype EmailCreate struct {\n\tshared.Action\n}\n\nfunc (a EmailCreate) GetName() flowpilot.ActionName {\n\treturn shared.ActionEmailCreate\n}\n\nfunc (a EmailCreate) GetDescription() string {\n\treturn \"Create an email address for the current session user.\"\n}\n\nfunc (a EmailCreate) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\n\tif !deps.Cfg.Email.Enabled || (ok && len(userModel.Emails) >= deps.Cfg.Email.Limit) {\n\t\tc.SuspendAction()\n\t} else {\n\t\tc.AddInputs(flowpilot.EmailInput(\"email\").Required(true).MaxLength(deps.Cfg.Email.MaxLength).TrimSpace(true).LowerCase(true))\n\t}\n}\n\nfunc (a EmailCreate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tnewEmailAddress := c.Input().Get(\"email\").String()\n\n\texistingEmailModel, err := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).FindByAddress(newEmailAddress)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not fetch email: %w\", err)\n\t}\n\n\tcurrentPrimaryEmail := userModel.Emails.GetPrimary().Address\n\n\tif existingEmailModel != nil {\n\t\tif (existingEmailModel.UserID != nil && existingEmailModel.UserID.String() == userModel.ID.String()) || !deps.Cfg.Email.RequireVerification || deps.Cfg.Privacy.ShowAccountExistenceHints {\n\t\t\tc.Input().SetError(\"email\", shared.ErrorEmailAlreadyExists)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t} else {\n\t\t\terr = c.CopyInputValuesToStash(\"email\")\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to copy email to stash: %w\", err)\n\t\t\t}\n\n\t\t\terr = c.Stash().Set(shared.StashPathUserID, userModel.ID.String())\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set user_id to stash: %w\", err)\n\t\t\t}\n\n\t\t\terr = c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailRegistrationAttempted)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t\t}\n\n\t\t\treturn c.Continue(shared.StatePasscodeConfirmation)\n\t\t}\n\t} else if deps.Cfg.Email.RequireVerification {\n\t\terr = c.CopyInputValuesToStash(\"email\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to copy email to stash: %w\", err)\n\t\t}\n\n\t\terr = c.Stash().Set(shared.StashPathUserID, userModel.ID.String())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set user_id to stash: %w\", err)\n\t\t}\n\n\t\terr = c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailVerification)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t}\n\n\t\treturn c.Continue(shared.StatePasscodeConfirmation, shared.StateProfileInit)\n\t} else {\n\t\temailModel := models.NewEmail(&userModel.ID, newEmailAddress)\n\n\t\terr = deps.Persister.GetEmailPersisterWithConnection(deps.Tx).Create(*emailModel)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not save email: %w\", err)\n\t\t}\n\n\t\tif len(userModel.Emails) == 0 {\n\t\t\t// The user has only one 1 email and it is the email we just added. It makes sense then,\n\t\t\t// to automatically set this as the primary email.\n\t\t\tprimaryEmailModel := models.NewPrimaryEmail(emailModel.ID, userModel.ID)\n\t\t\terr = deps.Persister.GetPrimaryEmailPersisterWithConnection(deps.Tx).Create(*primaryEmailModel)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not save primary email: %w\", err)\n\t\t\t}\n\t\t\temailModel.PrimaryEmail = primaryEmailModel\n\t\t}\n\n\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\tdeps.Tx,\n\t\t\tdeps.HttpContext,\n\t\t\tmodels.AuditLogEmailCreated,\n\t\t\t&models.User{ID: userModel.ID},\n\t\t\tnil,\n\t\t\tauditlog.Detail(\"email\", emailModel.Address),\n\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t}\n\n\t\tif deps.Cfg.SecurityNotifications.Notifications.EmailCreate.Enabled {\n\t\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\t\tEmailAddress: currentPrimaryEmail,\n\t\t\t\tTemplate:     \"email_create\",\n\t\t\t\tHttpContext:  deps.HttpContext,\n\t\t\t\tBodyData: map[string]interface{}{\n\t\t\t\t\t\"NewEmailAddress\": newEmailAddress,\n\t\t\t\t},\n\t\t\t\tData: &webhook.SecurityNotificationData{\n\t\t\t\t\tNewEmailAddress: newEmailAddress,\n\t\t\t\t},\n\t\t\t\tUserContext: *userModel,\n\t\t\t})\n\t\t}\n\n\t\tuserModel.Emails = append(userModel.Emails, *emailModel)\n\n\t\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserEmailCreate, userModel.ID)\n\n\t\treturn c.Continue(shared.StateProfileInit)\n\t}\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_delete.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype EmailDelete struct {\n\tshared.Action\n}\n\nfunc (a EmailDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionEmailDelete\n}\n\nfunc (a EmailDelete) GetDescription() string {\n\treturn \"Delete an email address.\"\n}\n\nfunc (a EmailDelete) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tinput := flowpilot.StringInput(\"email_id\").Required(true).Hidden(true)\n\n\tlastEmail := len(userModel.Emails) == 1\n\n\tcanDoPasskeyLogin := deps.Cfg.Passkey.Enabled && len(userModel.GetPasskeys()) > 0\n\tcanDoPWLogin := deps.Cfg.Password.Enabled && userModel.PasswordCredential != nil\n\tcanDoPasscode := deps.Cfg.Email.Enabled && deps.Cfg.Email.UseForAuthentication\n\n\tfor _, email := range userModel.Emails {\n\t\tif email.IsPrimary() {\n\t\t\tcanDoPWLoginWithUsername := canDoPWLogin && deps.Cfg.Username.UseAsLoginIdentifier && userModel.GetUsername() != nil\n\t\t\tif lastEmail && deps.Cfg.Email.Optional && (canDoPasskeyLogin || canDoPWLoginWithUsername) {\n\t\t\t\tinput.AllowedValue(email.Address, email.ID.String())\n\t\t\t}\n\t\t} else {\n\t\t\tif !canDoPasskeyLogin && !canDoPWLogin && !canDoPasscode {\n\t\t\t\tfor _, otherEmail := range userModel.Emails {\n\t\t\t\t\tif otherEmail.ID.String() == email.ID.String() {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif services.UserCanDoThirdParty(deps.Cfg, otherEmail.Identities) ||\n\t\t\t\t\t\tservices.UserCanDoSaml(deps.Cfg, otherEmail.Identities) {\n\t\t\t\t\t\tinput.AllowedValue(email.Address, email.ID.String())\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tinput.AllowedValue(email.Address, email.ID.String())\n\t\t\t}\n\t\t}\n\t}\n\n\tc.AddInputs(input)\n}\n\nfunc (a EmailDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tcurrentPrimaryEmail := userModel.Emails.GetPrimary().Address\n\n\temailToBeDeletedId := uuid.FromStringOrNil(c.Input().Get(\"email_id\").String())\n\temailToBeDeletedModel := userModel.GetEmailById(emailToBeDeletedId)\n\tif emailToBeDeletedModel == nil {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(errors.New(\"unknown email\")))\n\t}\n\n\tdeletedEmailAddress := emailToBeDeletedModel.Address\n\n\tif emailToBeDeletedModel.IsPrimary() {\n\t\tif !deps.Cfg.Email.Optional {\n\t\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted.Wrap(errors.New(\"cannot delete primary email\")))\n\t\t} else {\n\t\t\terr := deps.Persister.GetPrimaryEmailPersisterWithConnection(deps.Tx).Delete(*emailToBeDeletedModel.PrimaryEmail)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not delete primary email: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif deps.Cfg.SecurityNotifications.Notifications.EmailDelete.Enabled {\n\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\tEmailAddress: currentPrimaryEmail,\n\t\t\tTemplate:     \"email_delete\",\n\t\t\tHttpContext:  deps.HttpContext,\n\t\t\tBodyData: map[string]interface{}{\n\t\t\t\t\"DeletedEmailAddress\": deletedEmailAddress,\n\t\t\t},\n\t\t\tData: &webhook.SecurityNotificationData{\n\t\t\t\tDeletedEmailAddress: deletedEmailAddress,\n\t\t\t},\n\t\t\tUserContext: *userModel,\n\t\t})\n\t}\n\n\terr := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).Delete(*emailToBeDeletedModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not delete email: %w\", err)\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogEmailDeleted,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"email\", emailToBeDeletedModel.Address),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tuserModel.DeleteEmail(*emailToBeDeletedModel)\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserEmailDelete, userModel.ID)\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_set_primary.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype EmailSetPrimary struct {\n\tshared.Action\n}\n\nfunc (a EmailSetPrimary) GetName() flowpilot.ActionName {\n\treturn shared.ActionEmailSetPrimary\n}\n\nfunc (a EmailSetPrimary) GetDescription() string {\n\treturn \"Sets a an email address as the primary email address.\"\n}\n\nfunc (a EmailSetPrimary) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Email.Enabled {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif len(userModel.Emails) == 1 && userModel.Emails[0].IsPrimary() {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif len(userModel.Emails) == 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"email_id\").Required(true).Hidden(true))\n}\n\nfunc (a EmailSetPrimary) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\temailId := uuid.FromStringOrNil(c.Input().Get(\"email_id\").String())\n\temailModel := userModel.GetEmailById(emailId)\n\n\tif emailModel == nil {\n\t\treturn c.Error(shared.ErrorNotFound)\n\t}\n\n\tif emailModel.IsPrimary() {\n\t\treturn c.Continue(shared.StateProfileInit)\n\t}\n\n\tvar primaryEmail *models.PrimaryEmail\n\tvar existingPrimaryEmailAddress string\n\tif e := userModel.Emails.GetPrimary(); e != nil {\n\t\tprimaryEmail = e.PrimaryEmail\n\t\texistingPrimaryEmailAddress = e.Address\n\t}\n\n\tif primaryEmail == nil {\n\t\tprimaryEmail = models.NewPrimaryEmail(emailModel.ID, userModel.ID)\n\t\terr := deps.Persister.GetPrimaryEmailPersisterWithConnection(deps.Tx).Create(*primaryEmail)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store new primary email: %w\", err)\n\t\t}\n\t} else {\n\t\tprimaryEmail.EmailID = emailModel.ID\n\t\terr := deps.Persister.GetPrimaryEmailPersisterWithConnection(deps.Tx).Update(*primaryEmail)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to change primary email: %w\", err)\n\t\t}\n\t}\n\n\tif deps.Cfg.SecurityNotifications.Notifications.EmailCreate.Enabled && existingPrimaryEmailAddress != \"\" {\n\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\tEmailAddress: existingPrimaryEmailAddress,\n\t\t\tTemplate:     \"primary_email_update\",\n\t\t\tHttpContext:  deps.HttpContext,\n\t\t\tUserContext:  *userModel,\n\t\t\tBodyData: map[string]interface{}{\n\t\t\t\t\"OldEmailAddress\": existingPrimaryEmailAddress,\n\t\t\t\t\"NewEmailAddress\": emailModel.Address,\n\t\t\t},\n\t\t\tData: &webhook.SecurityNotificationData{\n\t\t\t\tOldEmailAddress: existingPrimaryEmailAddress,\n\t\t\t\tNewEmailAddress: emailModel.Address,\n\t\t\t},\n\t\t})\n\t}\n\n\terr := deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogPrimaryEmailChanged,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"email\", emailModel.Address),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tuserModel.SetPrimaryEmail(primaryEmail)\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserEmailPrimary, userModel.ID)\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_verify.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype EmailVerify struct {\n\tshared.Action\n}\n\nfunc (a EmailVerify) GetName() flowpilot.ActionName {\n\treturn shared.ActionEmailVerify\n}\n\nfunc (a EmailVerify) GetDescription() string {\n\treturn \"Verify an email.\"\n}\n\nfunc (a EmailVerify) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Email.Enabled {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif !userModel.Emails.HasUnverified() {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"email_id\").Required(true).Hidden(true))\n}\n\nfunc (a EmailVerify) Execute(c flowpilot.ExecutionContext) error {\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\temailModel := userModel.GetEmailById(uuid.FromStringOrNil(c.Input().Get(\"email_id\").String()))\n\tif emailModel == nil {\n\t\treturn c.Error(shared.ErrorNotFound)\n\t}\n\n\terr := c.Stash().Set(shared.StashPathEmail, emailModel.Address)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set email address to verify to stash: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserID, userModel.ID.String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_id to stash: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailVerification)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set passcode_tempalte to stash %w\", err)\n\t}\n\n\treturn c.Continue(shared.StatePasscodeConfirmation, shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_exchange_token.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n)\n\ntype ExchangeToken struct {\n\tshared.Action\n}\n\nfunc (a ExchangeToken) GetName() flowpilot.ActionName {\n\treturn shared.ActionExchangeToken // TODO: should we rename it?\n}\n\nfunc (a ExchangeToken) GetDescription() string {\n\treturn \"Exchange a token for linking the identity to a user account.\"\n}\n\nfunc (a ExchangeToken) Initialize(c flowpilot.InitializationContext) {\n\tc.AddInputs(\n\t\tflowpilot.StringInput(\"token\").Hidden(true).Required(true),\n\t\tflowpilot.StringInput(\"code_verifier\").Hidden(true),\n\t)\n}\n\nfunc (a ExchangeToken) Execute(c flowpilot.ExecutionContext) error {\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tdeps := a.GetDeps(c)\n\n\tif deps.Cfg.RateLimiter.Enabled {\n\t\trateLimitKey := rate_limiter.CreateRateLimitTokenExchangeKey(deps.HttpContext.RealIP())\n\t\tretryAfterSeconds, ok, err := rate_limiter.Limit2(deps.TokenExchangeRateLimiter, rateLimitKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rate limiter failed: %w\", err)\n\t\t}\n\n\t\tif !ok {\n\t\t\terr = c.Payload().Set(\"retry_after\", retryAfterSeconds)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set a value for retry_after to the payload: %w\", err)\n\t\t\t}\n\t\t\treturn c.Error(shared.ErrorRateLimitExceeded.Wrap(fmt.Errorf(\"rate limit exceeded for: %s\", rateLimitKey)))\n\t\t}\n\t}\n\n\ttokenModel, err := deps.Persister.GetTokenPersisterWithConnection(deps.Tx).GetByValue(c.Input().Get(\"token\").String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch token from db: %w\", err)\n\t}\n\n\tif tokenModel == nil {\n\t\treturn errors.New(\"token not found\")\n\t}\n\n\tif tokenModel.PKCECodeVerifier != nil && *tokenModel.PKCECodeVerifier != \"\" && *tokenModel.PKCECodeVerifier != c.Input().Get(\"code_verifier\").String() {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(errors.New(\"code_verifier does not match\")))\n\t}\n\n\tif time.Now().UTC().After(tokenModel.ExpiresAt) {\n\t\treturn errors.New(\"token expired\")\n\t}\n\n\tuser, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tif user.ID != tokenModel.UserID {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted.Wrap(\n\t\t\tfmt.Errorf(\"token does not belong to the current user. current user: %s, token user: %s\", user.ID, tokenModel.UserID),\n\t\t))\n\t}\n\n\tidentity, err := deps.Persister.GetIdentityPersisterWithConnection(deps.Tx).GetByID(*tokenModel.IdentityID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch identity from db: %w\", err)\n\t}\n\n\tif identity.UserID != nil {\n\t\treturn flowpilot.ErrorOperationNotPermitted.Wrap(errors.New(\"identity is already linked to a user\"))\n\t}\n\n\t// link the identity to the user\n\tidentity.UserID = &user.ID\n\n\terr = deps.Persister.GetIdentityPersisterWithConnection(deps.Tx).Update(*identity)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update identity: %w\", err)\n\t}\n\n\terr = deps.Persister.GetTokenPersisterWithConnection(deps.Tx).Delete(*tokenModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete token from db: %w\", err)\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_otp_secret_delete.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype OTPSecretDelete struct {\n\tshared.Action\n}\n\nfunc (a OTPSecretDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionOTPSecretDelete\n}\n\nfunc (a OTPSecretDelete) GetDescription() string {\n\treturn \"Delete an OTP secret\"\n}\n\nfunc (a OTPSecretDelete) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif userModel.OTPSecret == nil {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif deps.Cfg.MFA.Enabled && !deps.Cfg.MFA.Optional && len(userModel.GetSecurityKeys()) <= 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n}\n\nfunc (a OTPSecretDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\terr := deps.Persister.GetOTPSecretPersisterWithConnection(deps.Tx).Delete(userModel.OTPSecret)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not delete otp secret: %w\", err)\n\t}\n\n\tuserModel.DeleteOTPSecret()\n\n\t// Inform user that an MFA method has been deleted\n\tif deps.Cfg.SecurityNotifications.Notifications.MFADelete.Enabled {\n\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\tEmailAddress: userModel.Emails.GetPrimary().Address,\n\t\t\tTemplate:     \"mfa_delete\",\n\t\t\tHttpContext:  deps.HttpContext,\n\t\t\tUserContext:  *userModel,\n\t\t})\n\t}\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_password_create.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype PasswordCreate struct {\n\tshared.Action\n}\n\nfunc (a PasswordCreate) GetName() flowpilot.ActionName {\n\treturn shared.ActionPasswordCreate\n}\n\nfunc (a PasswordCreate) GetDescription() string {\n\treturn \"Create a new password.\"\n}\n\nfunc (a PasswordCreate) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, _ := c.Get(\"session_user\").(*models.User)\n\n\tif !deps.Cfg.Password.Enabled {\n\t\tc.SuspendAction()\n\t}\n\n\tif userModel.PasswordCredential != nil {\n\t\t// The password_update action must be used instead\n\t\tc.SuspendAction()\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"password\").\n\t\tRequired(true).\n\t\tMinLength(deps.Cfg.Password.MinLength).\n\t\tMaxLength(72),\n\t)\n\n}\n\nfunc (a PasswordCreate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tpassword := c.Input().Get(\"password\").String()\n\n\tpasswordCredential := models.NewPasswordCredential(userModel.ID, password) // ?\n\n\terr := deps.PasswordService.CreatePassword(deps.Tx, userModel.ID, password) // ?\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not set password: %w\", err)\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogPasswordChanged,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"context\", \"profile\"),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tuserModel.PasswordCredential = passwordCredential\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_password_delete.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype PasswordDelete struct {\n\tshared.Action\n}\n\nfunc (a PasswordDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionPasswordDelete\n}\n\nfunc (a PasswordDelete) GetDescription() string {\n\treturn \"Delete a password.\"\n}\n\nfunc (a PasswordDelete) Initialize(c flowpilot.InitializationContext) {\n\tif a.mustSuspend(c) {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n}\n\nfunc (a PasswordDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tpasswordCredentialModel, err := deps.Persister.GetPasswordCredentialPersisterWithConnection(deps.Tx).GetByUserID(userModel.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not fetch password credential: %w\", err)\n\t}\n\n\tif passwordCredentialModel == nil {\n\t\treturn c.Continue(shared.StateProfileInit)\n\t}\n\n\terr = deps.Persister.GetPasswordCredentialPersisterWithConnection(deps.Tx).Delete(*passwordCredentialModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not delete password credential: %w\", err)\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogPasswordDeleted,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tuserModel.PasswordCredential = nil\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n\nfunc (a PasswordDelete) mustSuspend(c flowpilot.Context) bool {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Password.Enabled {\n\t\treturn true\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn true\n\t}\n\n\tif userModel.PasswordCredential == nil || !deps.Cfg.Password.Optional {\n\t\treturn true\n\t}\n\n\tidentities := userModel.GetIdentities()\n\n\tcanDoWebauthn := deps.Cfg.Passkey.Enabled && len(userModel.WebauthnCredentials) > 0\n\tcanUseUsernameAsLoginIdentifier := deps.Cfg.Username.UseAsLoginIdentifier && userModel.Username != nil\n\tcanUseEmailAsLoginIdentifier := deps.Cfg.Email.UseAsLoginIdentifier && len(userModel.Emails) > 0\n\tcanDoPasscode := deps.Cfg.Email.Enabled && deps.Cfg.Email.UseForAuthentication && (canUseEmailAsLoginIdentifier || canUseUsernameAsLoginIdentifier && len(userModel.Emails) > 0)\n\tcanDoThirdParty := services.UserCanDoThirdParty(deps.Cfg, identities) || services.UserCanDoSaml(deps.Cfg, identities)\n\tcanUseNoOtherAuthMethod := !canDoWebauthn && !canDoPasscode && !canDoThirdParty\n\n\tif canUseNoOtherAuthMethod {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_password_update.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype PasswordUpdate struct {\n\tshared.Action\n}\n\nfunc (a PasswordUpdate) GetName() flowpilot.ActionName {\n\treturn shared.ActionPasswordUpdate\n}\n\nfunc (a PasswordUpdate) GetDescription() string {\n\treturn \"Update an existing password.\"\n}\n\nfunc (a PasswordUpdate) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, _ := c.Get(\"session_user\").(*models.User)\n\n\tif !deps.Cfg.Password.Enabled {\n\t\tc.SuspendAction()\n\t}\n\n\tif userModel.PasswordCredential == nil {\n\t\t// The password_create action must be used instead\n\t\tc.SuspendAction()\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"password\").\n\t\tRequired(true).\n\t\tMinLength(deps.Cfg.Password.MinLength).\n\t\tMaxLength(72))\n}\n\nfunc (a PasswordUpdate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tpassword := c.Input().Get(\"password\").String()\n\n\terr := deps.PasswordService.UpdatePassword(deps.Tx, userModel.PasswordCredential, password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not udate password: %w\", err)\n\t}\n\n\tif deps.Cfg.SecurityNotifications.Notifications.PasswordUpdate.Enabled {\n\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\tEmailAddress: userModel.Emails.GetPrimary().Address,\n\t\t\tTemplate:     \"password_update\",\n\t\t\tHttpContext:  deps.HttpContext,\n\t\t\tUserContext:  *userModel,\n\t\t})\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogPasswordChanged,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"context\", \"profile\"),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserPasswordChange, userModel.ID)\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_patch_metadata.go",
    "content": "package profile\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\n\tjsonpatch \"github.com/evanphx/json-patch/v5\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype PatchMetadata struct {\n\tshared.Action\n}\n\nfunc (a PatchMetadata) GetName() flowpilot.ActionName {\n\treturn shared.ActionPatchMetadata\n}\n\nfunc (a PatchMetadata) GetDescription() string {\n\treturn \"Patch the (unsafe) metadata of the user\"\n}\n\nfunc (a PatchMetadata) Initialize(c flowpilot.InitializationContext) {\n\tc.AddInputs(flowpilot.JSONInput(\"patch_metadata\").Required(true).Hidden(false))\n}\n\nfunc (a PatchMetadata) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tpatchMetadata := c.Input().Get(\"patch_metadata\").String()\n\n\tlooksLikeObject := strings.HasPrefix(patchMetadata, \"{\") && strings.HasSuffix(patchMetadata, \"}\")\n\tif patchMetadata == \"\" || (patchMetadata != \"null\" && !looksLikeObject) {\n\t\tc.Input().SetError(\n\t\t\t\"patch_metadata\",\n\t\t\tshared.ErrorInvalidMetadata.Wrap(errors.New(\"patch metadata must be an object or null\")))\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tuserMetadataModel, err := deps.Persister.GetUserMetadataPersisterWithConnection(deps.Tx).Get(userModel.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not fetch user metadata: %w\", err)\n\t}\n\n\tif patchMetadata == \"null\" {\n\t\tuserMetadataModel.Unsafe = nulls.String{}\n\t} else {\n\t\tcurrentUnsafeMetadata := \"{}\"\n\t\tif userMetadataModel.Unsafe.Valid {\n\t\t\tcurrentUnsafeMetadata = userMetadataModel.Unsafe.String\n\t\t}\n\n\t\tpatchedUnsafeMetadataBytes, err := jsonpatch.MergePatch(\n\t\t\t[]byte(currentUnsafeMetadata),\n\t\t\t[]byte(patchMetadata),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could patch unsafe metadata: %w\", err)\n\t\t}\n\n\t\tif !json.Valid(patchedUnsafeMetadataBytes) {\n\t\t\treturn fmt.Errorf(\"invalid metadata JSON after applying merge patch\")\n\t\t}\n\n\t\tuserMetadataModel.Unsafe = nulls.String{\n\t\t\tValid:  true,\n\t\t\tString: string(patchedUnsafeMetadataBytes),\n\t\t}\n\t}\n\n\terr = deps.Persister.GetUserMetadataPersisterWithConnection(deps.Tx).Update(userMetadataModel)\n\tif err != nil {\n\t\tif persistence.IsMetadataLimitExceededError(err) {\n\t\t\tc.Input().SetError(\n\t\t\t\t\"patch_metadata\",\n\t\t\t\tshared.ErrorInvalidMetadata.Wrap(errors.New(\"metadata must not exceed character limit (3000)\")))\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t}\n\n\t\treturn fmt.Errorf(\"could not save metadata: %w\", err)\n\t}\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_security_key_create.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SecurityKeyCreate struct {\n\tshared.Action\n}\n\nfunc (a SecurityKeyCreate) GetName() flowpilot.ActionName {\n\treturn shared.ActionSecurityKeyCreate\n}\n\nfunc (a SecurityKeyCreate) GetDescription() string {\n\treturn \"Get WebAuthn creation options to register a WebAuthn credential.\"\n}\n\nfunc (a SecurityKeyCreate) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif !deps.Cfg.MFA.Enabled || !deps.Cfg.MFA.SecurityKeys.Enabled {\n\t\tc.SuspendAction()\n\t}\n\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() {\n\t\tc.SuspendAction()\n\t}\n\n\tif len(userModel.GetSecurityKeys()) >= deps.Cfg.MFA.SecurityKeys.Limit {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n}\n\nfunc (a SecurityKeyCreate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tprimaryEmailModel := userModel.Emails.GetPrimary()\n\tif primaryEmailModel == nil && userModel.Username == nil {\n\t\treturn errors.New(\"user must have either email or username\")\n\t}\n\n\tvar primaryEmailAddress string\n\tif primaryEmailModel != nil {\n\t\tprimaryEmailAddress = primaryEmailModel.Address\n\t}\n\n\tparams := services.GenerateCreationOptionsParams{\n\t\tTx:       deps.Tx,\n\t\tUserID:   userModel.ID,\n\t\tEmail:    &primaryEmailAddress,\n\t\tUsername: userModel.GetUsername(),\n\t}\n\n\tsessionDataModel, creationOptions, err := deps.WebauthnService.GenerateCreationOptionsSecurityKey(params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate webauthn creation options: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Stash().Set(shared.StashPathCreateMFAOnlyCredential, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Payload().Set(\"creation_options\", creationOptions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue(shared.StateProfileWebauthnCredentialVerification)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_security_key_delete.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SecurityKeyDelete struct {\n\tshared.Action\n}\n\nfunc (a SecurityKeyDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionSecurityKeyDelete\n}\n\nfunc (a SecurityKeyDelete) GetDescription() string {\n\treturn \"Delete a security key\"\n}\n\nfunc (a SecurityKeyDelete) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif !deps.Cfg.MFA.Enabled || !deps.Cfg.MFA.SecurityKeys.Enabled {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif len(userModel.GetSecurityKeys()) <= 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif deps.Cfg.MFA.Enabled && !deps.Cfg.MFA.Optional &&\n\t\tuserModel.OTPSecret == nil && len(userModel.GetSecurityKeys()) == 1 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"security_key_id\").Required(true).Hidden(true))\n}\n\nfunc (a SecurityKeyDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\twebauthnCredentialModel := userModel.GetWebauthnCredentialById(c.Input().Get(\"security_key_id\").String())\n\tif webauthnCredentialModel == nil {\n\t\treturn c.Error(shared.ErrorNotFound)\n\t}\n\n\terr := deps.Persister.GetWebauthnCredentialPersisterWithConnection(deps.Tx).Delete(*webauthnCredentialModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not delete security key: %w\", err)\n\t}\n\n\tuserModel.DeleteWebauthnCredential(webauthnCredentialModel.ID)\n\n\t// Inform user that an MFA method has been deleted\n\tif deps.Cfg.SecurityNotifications.Notifications.MFADelete.Enabled {\n\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\tEmailAddress: userModel.Emails.GetPrimary().Address,\n\t\t\tTemplate:     \"mfa_delete\",\n\t\t\tHttpContext:  deps.HttpContext,\n\t\t\tUserContext:  *userModel,\n\t\t})\n\t}\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_session_delete.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SessionDelete struct {\n\tshared.Action\n}\n\nfunc (a SessionDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionSessionDelete\n}\n\nfunc (a SessionDelete) GetDescription() string {\n\treturn \"Delete a session.\"\n}\n\nfunc (a SessionDelete) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\tif !deps.Cfg.Session.AllowRevocation {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tinput := flowpilot.StringInput(\"session_id\").Required(true).Hidden(true)\n\n\tcurrentSessionID := uuid.FromStringOrNil(c.Get(\"session_id\").(string))\n\tsessions, err := deps.Persister.GetSessionPersisterWithConnection(deps.Tx).ListActive(userModel.ID)\n\tif err != nil {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tvar deletableSessions []string\n\tfor _, session := range sessions {\n\t\tif session.ID != currentSessionID {\n\t\t\tdeletableSessions = append(deletableSessions, session.ID.String())\n\t\t}\n\t}\n\n\tif len(deletableSessions) < 1 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tfor _, deletableSession := range deletableSessions {\n\t\tinput.AllowedValue(deletableSession, deletableSession)\n\t}\n\n\tc.AddInputs(input)\n}\n\nfunc (a SessionDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tsessionToBeDeleted := uuid.FromStringOrNil(c.Input().Get(\"session_id\").String())\n\n\tsession, err := deps.Persister.GetSessionPersisterWithConnection(deps.Tx).Get(sessionToBeDeleted)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get session from db: %w\", err)\n\t}\n\n\tif session != nil {\n\t\terr = deps.Persister.GetSessionPersisterWithConnection(deps.Tx).Delete(*session)\n\t}\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_username_create.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype UsernameCreate struct {\n\tshared.Action\n}\n\nfunc (a UsernameCreate) GetName() flowpilot.ActionName {\n\treturn shared.ActionUsernameCreate\n}\n\nfunc (a UsernameCreate) GetDescription() string {\n\treturn \"Create a new username.\"\n}\n\nfunc (a UsernameCreate) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif !deps.Cfg.Username.Enabled || userModel.Username != nil {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"username\").\n\t\tPreserve(true).\n\t\tRequired(true).\n\t\tMinLength(deps.Cfg.Username.MinLength).\n\t\tMaxLength(deps.Cfg.Username.MaxLength).\n\t\tTrimSpace(true).\n\t\tLowerCase(true))\n}\n\nfunc (a UsernameCreate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tusername := c.Input().Get(\"username\").String()\n\n\tif !services.ValidateUsername(username) {\n\t\tc.Input().SetError(\"username\", shared.ErrorInvalidUsername)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tduplicateUsername, err := deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).GetByName(username)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user from db: %w\", err)\n\t}\n\n\tif duplicateUsername != nil && duplicateUsername.ID.String() != userModel.ID.String() {\n\t\tc.Input().SetError(\"username\", shared.ErrorUsernameAlreadyExists)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tusernameModel := models.NewUsername(userModel.ID, username)\n\terr = deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).Create(*usernameModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create username: %w\", err)\n\t}\n\tuserModel.SetUsername(usernameModel)\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogUsernameChanged,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"username\", usernameModel.Username),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserUsernameCreate, userModel.ID)\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_username_delete.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype UsernameDelete struct {\n\tshared.Action\n}\n\nfunc (a UsernameDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionUsernameDelete\n}\n\nfunc (a UsernameDelete) GetDescription() string {\n\treturn \"Delete the username of a user.\"\n}\n\nfunc (a UsernameDelete) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tcanDoPasskeyLogin := deps.Cfg.Passkey.Enabled && len(userModel.GetPasskeys()) > 0\n\n\tif !deps.Cfg.Username.Enabled ||\n\t\t!deps.Cfg.Username.Optional ||\n\t\tuserModel.Username == nil ||\n\t\t(len(userModel.Emails) == 0 && !canDoPasskeyLogin) {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a UsernameDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tusernameModel := &models.Username{ID: userModel.Username.ID}\n\terr := deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).Delete(usernameModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete username from db: %w\", err)\n\t}\n\tdeletedUsername := userModel.GetUsername()\n\tuserModel.DeleteUsername()\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogUsernameDeleted,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"username\", *deletedUsername),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserUsernameDelete, userModel.ID)\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_username_update.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype UsernameUpdate struct {\n\tshared.Action\n}\n\nfunc (a UsernameUpdate) GetName() flowpilot.ActionName {\n\treturn shared.ActionUsernameUpdate\n}\n\nfunc (a UsernameUpdate) GetDescription() string {\n\treturn \"Update an existing username.\"\n}\n\nfunc (a UsernameUpdate) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif !deps.Cfg.Username.Enabled || userModel.Username == nil {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"username\").\n\t\tPreserve(true).\n\t\tRequired(true).\n\t\tMinLength(deps.Cfg.Username.MinLength).\n\t\tMaxLength(deps.Cfg.Username.MaxLength).\n\t\tTrimSpace(true).\n\t\tLowerCase(true))\n}\n\nfunc (a UsernameUpdate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tusername := c.Input().Get(\"username\").String()\n\n\tif !services.ValidateUsername(username) {\n\t\tc.Input().SetError(\"username\", shared.ErrorInvalidUsername)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tduplicateUsername, err := deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).GetByName(username)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user from db: %w\", err)\n\t}\n\n\tif duplicateUsername != nil && duplicateUsername.ID.String() != userModel.ID.String() {\n\t\tc.Input().SetError(\"username\", shared.ErrorUsernameAlreadyExists)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tusernameModel := &models.Username{\n\t\tID:       userModel.Username.ID,\n\t\tUserId:   userModel.ID,\n\t\tUsername: username,\n\t}\n\n\terr = deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).Update(usernameModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update username: %w\", err)\n\t}\n\tuserModel.SetUsername(usernameModel)\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogUsernameChanged,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"username\", usernameModel.Username),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserUsernameUpdate, userModel.ID)\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_webauthn_credential_create.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnCredentialCreate struct {\n\tshared.Action\n}\n\nfunc (a WebauthnCredentialCreate) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnCredentialCreate\n}\n\nfunc (a WebauthnCredentialCreate) GetDescription() string {\n\treturn \"Create a Webauthn credential for the current session user.\"\n}\n\nfunc (a WebauthnCredentialCreate) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\n\tif !deps.Cfg.Passkey.Enabled || !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() || (ok && len(userModel.WebauthnCredentials) >= deps.Cfg.Passkey.Limit) {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a WebauthnCredentialCreate) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tprimaryEmailModel := userModel.Emails.GetPrimary()\n\tif primaryEmailModel == nil && userModel.Username == nil {\n\t\treturn errors.New(\"user must have either email or username\")\n\t}\n\n\tvar primaryEmailAddress string\n\tif primaryEmailModel != nil {\n\t\tprimaryEmailAddress = primaryEmailModel.Address\n\t}\n\n\tparams := services.GenerateCreationOptionsParams{\n\t\tTx:       deps.Tx,\n\t\tUserID:   userModel.ID,\n\t\tEmail:    &primaryEmailAddress,\n\t\tUsername: userModel.GetUsername(),\n\t}\n\n\tsessionDataModel, creationOptions, err := deps.WebauthnService.GenerateCreationOptionsPasskey(params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate webauthn creation options: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathWebauthnSessionDataID, sessionDataModel.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Stash().Set(shared.StashPathCreateMFAOnlyCredential, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Payload().Set(\"creation_options\", creationOptions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue(shared.StateProfileWebauthnCredentialVerification)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_webauthn_credential_delete.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnCredentialDelete struct {\n\tshared.Action\n}\n\nfunc (a WebauthnCredentialDelete) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnCredentialDelete\n}\n\nfunc (a WebauthnCredentialDelete) GetDescription() string {\n\treturn \"Delete a Webauthn credential.\"\n}\n\nfunc (a WebauthnCredentialDelete) Initialize(c flowpilot.InitializationContext) {\n\tif a.mustSuspend(c) {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"passkey_id\").Required(true).Hidden(true))\n}\n\nfunc (a WebauthnCredentialDelete) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\twebauthnCredentialModel := userModel.GetWebauthnCredentialById(c.Input().Get(\"passkey_id\").String())\n\tif webauthnCredentialModel == nil {\n\t\treturn c.Error(shared.ErrorNotFound)\n\t}\n\n\terr := deps.Persister.GetWebauthnCredentialPersisterWithConnection(deps.Tx).Delete(*webauthnCredentialModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not delete passkey: %w\", err)\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogPasskeyDeleted,\n\t\t&models.User{ID: userModel.ID},\n\t\tnil,\n\t\tauditlog.Detail(\"credential_id\", webauthnCredentialModel.ID),\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\tuserModel.DeleteWebauthnCredential(webauthnCredentialModel.ID)\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n\nfunc (a WebauthnCredentialDelete) mustSuspend(c flowpilot.Context) bool {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Passkey.Enabled {\n\t\treturn true\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn true\n\t}\n\n\tif len(userModel.WebauthnCredentials) == 0 {\n\t\treturn true\n\t}\n\n\tidentities := userModel.GetIdentities()\n\n\tisLastWebauthnCredential := len(userModel.WebauthnCredentials) == 1\n\n\tif isLastWebauthnCredential && !deps.Cfg.Passkey.Optional {\n\t\treturn true\n\t}\n\n\tcanUseUsernameAsLoginIdentifier := deps.Cfg.Username.UseAsLoginIdentifier && userModel.Username != nil\n\tcanUseEmailAsLoginIdentifier := deps.Cfg.Email.UseAsLoginIdentifier && len(userModel.Emails) > 0\n\tcanDoPassword := deps.Cfg.Password.Enabled && userModel.PasswordCredential != nil && (canUseUsernameAsLoginIdentifier || canUseEmailAsLoginIdentifier)\n\tcanDoPasscode := deps.Cfg.Email.Enabled && deps.Cfg.Email.UseForAuthentication && (canUseEmailAsLoginIdentifier || canUseUsernameAsLoginIdentifier && len(userModel.Emails) > 0)\n\tcanDoThirdParty := services.UserCanDoThirdParty(deps.Cfg, identities) || services.UserCanDoSaml(deps.Cfg, identities)\n\tcanUseNoOtherAuthMethod := !canDoPassword && !canDoThirdParty && !canDoPasscode\n\n\tif isLastWebauthnCredential && canUseNoOtherAuthMethod {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_webauthn_credential_rename.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnCredentialRename struct {\n\tshared.Action\n}\n\nfunc (a WebauthnCredentialRename) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnCredentialRename\n}\n\nfunc (a WebauthnCredentialRename) GetDescription() string {\n\treturn \"Rename a Webauthn credential.\"\n}\n\nfunc (a WebauthnCredentialRename) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Passkey.Enabled {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif len(userModel.WebauthnCredentials) == 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tc.AddInputs(flowpilot.StringInput(\"passkey_id\").Required(true).Hidden(true))\n\tc.AddInputs(flowpilot.StringInput(\"passkey_name\").Required(true))\n}\n\nfunc (a WebauthnCredentialRename) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\twebauthnCredentialModel := userModel.GetWebauthnCredentialById(c.Input().Get(\"passkey_id\").String())\n\tif webauthnCredentialModel == nil {\n\t\treturn c.Error(shared.ErrorNotFound)\n\t}\n\n\twebauthnCredentialName := c.Input().Get(\"passkey_name\").String()\n\twebauthnCredentialModel.Name = &webauthnCredentialName\n\n\terr := deps.Persister.GetWebauthnCredentialPersisterWithConnection(deps.Tx).Update(*webauthnCredentialModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not update credential: %w\", err)\n\t}\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_webauthn_verify_attestation_response.go",
    "content": "package profile\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnVerifyAttestationResponse struct {\n\tshared.Action\n}\n\nfunc (a WebauthnVerifyAttestationResponse) GetName() flowpilot.ActionName {\n\treturn shared.ActionWebauthnVerifyAttestationResponse\n}\n\nfunc (a WebauthnVerifyAttestationResponse) GetDescription() string {\n\treturn \"Send the result which was generated by creating a webauthn credential.\"\n}\n\nfunc (a WebauthnVerifyAttestationResponse) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() || (!deps.Cfg.Passkey.Enabled && !(deps.Cfg.MFA.Enabled && deps.Cfg.MFA.SecurityKeys.Enabled)) {\n\t\tc.SuspendAction()\n\t}\n\n\tc.AddInputs(flowpilot.JSONInput(\"public_key\").Required(true))\n}\n\nfunc (a WebauthnVerifyAttestationResponse) Execute(c flowpilot.ExecutionContext) error {\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn c.Error(flowpilot.ErrorOperationNotPermitted)\n\t}\n\n\tvar email string\n\tif primaryEmailModel := userModel.Emails.GetPrimary(); primaryEmailModel != nil {\n\t\temail = primaryEmailModel.Address\n\t}\n\n\terr := c.Stash().Set(shared.StashPathEmail, email)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_id to the stash: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserID, userModel.ID.String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_id to the stash: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUsername, userModel.GetUsername())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_id to the stash: %w\", err)\n\t}\n\n\terr = c.ExecuteHook(shared.VerifyAttestationResponse{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue(shared.StateProfileInit)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/hook_get_profile_data.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype GetProfileData struct {\n\tshared.Action\n}\n\nfunc (h GetProfileData) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn errors.New(\"no valid session\")\n\t}\n\n\tprofileData := dto.ProfileDataFromUserModel(userModel, &deps.Cfg)\n\n\terr := c.Payload().Set(\"user\", profileData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user payload: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/hook_get_sessions.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype GetSessions struct {\n\tshared.Action\n}\n\nfunc (h GetSessions) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif !deps.Cfg.Session.ShowOnProfile {\n\t\treturn nil\n\t}\n\n\tuserModel, ok := c.Get(\"session_user\").(*models.User)\n\tif !ok {\n\t\treturn errors.New(\"no valid session\")\n\t}\n\n\tactiveSessions, err := deps.Persister.GetSessionPersisterWithConnection(deps.Tx).ListActive(userModel.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get sessions from db: %w\", err)\n\t}\n\n\tcurrentSessionID := uuid.FromStringOrNil(c.Get(\"session_id\").(string))\n\n\tsessionsDto := make([]dto.SessionData, len(activeSessions))\n\tfor i := range activeSessions {\n\t\tsessionsDto[i] = dto.FromSessionModel(activeSessions[i], activeSessions[i].ID == currentSessionID)\n\t}\n\n\terr = c.Payload().Set(\"sessions\", sessionsDto)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set sessions payload: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/profile/hook_refresh_session_user.go",
    "content": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype RefreshSessionUser struct {\n\tshared.Action\n}\n\nfunc (h RefreshSessionUser) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tsessionToken, ok := deps.HttpContext.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"failed to cast session object\")\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse userId from JWT subject: %w\", err)\n\t}\n\n\tuserModel, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch user: %w\", err)\n\t}\n\n\tif userModel != nil {\n\t\tc.Set(\"session_user\", userModel)\n\t}\n\n\tsessionId, found := sessionToken.Get(\"session_id\")\n\tif found {\n\t\tc.Set(\"session_id\", sessionId)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/registration/action_register_login_identifier.go",
    "content": "package registration\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\n// RegisterLoginIdentifier takes the identifier which the user entered and checks if they are valid and available according to the configuration\ntype RegisterLoginIdentifier struct {\n\tshared.Action\n}\n\nfunc (a RegisterLoginIdentifier) GetName() flowpilot.ActionName {\n\treturn shared.ActionRegisterLoginIdentifier\n}\n\nfunc (a RegisterLoginIdentifier) GetDescription() string {\n\treturn \"Enter an identifier to register.\"\n}\n\nfunc (a RegisterLoginIdentifier) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Account.AllowSignup {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif (!deps.Cfg.Email.Enabled || (deps.Cfg.Email.Enabled && !deps.Cfg.Email.AcquireOnRegistration)) &&\n\t\t(!deps.Cfg.Username.Enabled || (deps.Cfg.Username.Enabled && !deps.Cfg.Username.AcquireOnRegistration)) {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tif deps.Cfg.Email.Enabled && deps.Cfg.Email.AcquireOnRegistration {\n\t\tinput := flowpilot.EmailInput(\"email\").\n\t\t\tMaxLength(deps.Cfg.Email.MaxLength).\n\t\t\tRequired(!deps.Cfg.Email.Optional).\n\t\t\tTrimSpace(true).\n\t\t\tLowerCase(true)\n\n\t\tc.AddInputs(input)\n\t}\n\n\tif deps.Cfg.Username.Enabled && deps.Cfg.Username.AcquireOnRegistration {\n\t\tinput := flowpilot.StringInput(\"username\").\n\t\t\tMinLength(deps.Cfg.Username.MinLength).\n\t\t\tMaxLength(deps.Cfg.Username.MaxLength).\n\t\t\tRequired(!deps.Cfg.Username.Optional).\n\t\t\tTrimSpace(true).\n\t\t\tLowerCase(true)\n\n\t\tc.AddInputs(input)\n\t}\n}\n\nfunc (a RegisterLoginIdentifier) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\temail := c.Input().Get(\"email\").String()\n\tusername := c.Input().Get(\"username\").String()\n\n\tif deps.Cfg.Email.Optional && len(email) == 0 &&\n\t\tdeps.Cfg.Username.Optional && len(username) == 0 {\n\t\terr := errors.New(\"either email or username must be provided\")\n\t\tc.Input().SetError(\"username\", flowpilot.ErrorValueInvalid.Wrap(err))\n\t\tc.Input().SetError(\"email\", flowpilot.ErrorValueInvalid.Wrap(err))\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(err))\n\t}\n\n\tif username != \"\" && (deps.Cfg.Username.Enabled && deps.Cfg.Username.AcquireOnRegistration) {\n\t\tif !services.ValidateUsername(username) {\n\t\t\tc.Input().SetError(\"username\", shared.ErrorInvalidUsername)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t}\n\n\t\t// Check that username is not already taken\n\t\t// this check is non-exhaustive as the username is not blocked here and might be created after the check here and the user creation\n\t\tuserModel, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).GetByUsername(username)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif userModel != nil {\n\t\t\tc.Input().SetError(\"username\", shared.ErrorUsernameAlreadyExists)\n\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t}\n\n\t\terr = c.CopyInputValuesToStash(\"username\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to copy username input to the stash: %w\", err)\n\t\t}\n\t}\n\n\tif email != \"\" {\n\t\tif deps.Cfg.Saml.Enabled {\n\t\t\tdomain := strings.Split(email, \"@\")[1]\n\t\t\tif provider, err := deps.SamlService.GetProviderByDomain(domain); err == nil && provider != nil {\n\t\t\t\tvar authUrl string\n\t\t\t\tauthUrl, err = deps.SamlService.GetAuthUrl(provider, deps.Cfg.Saml.DefaultRedirectUrl, true)\n\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to get auth url: %w\", err)\n\t\t\t\t}\n\n\t\t\t\t_ = c.Payload().Set(\"redirect_url\", authUrl)\n\n\t\t\t\treturn c.Continue(shared.StateThirdParty)\n\t\t\t}\n\t\t}\n\n\t\tif deps.Cfg.Email.Enabled && deps.Cfg.Email.AcquireOnRegistration {\n\t\t\t// Check that email is not already taken\n\t\t\t// this check is non-exhaustive as the email is not blocked here and might be created after the check here and the user creation\n\t\t\temailModel, err := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).FindByAddress(email)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// Do not return an error when only identifier is email and email verification is on (account enumeration protection) and privacy setting is off\n\t\t\tif emailModel != nil {\n\t\t\t\t// E-mail address already exists\n\t\t\t\tif !deps.Cfg.Email.RequireVerification || deps.Cfg.Privacy.ShowAccountExistenceHints {\n\t\t\t\t\tc.Input().SetError(\"email\", shared.ErrorEmailAlreadyExists)\n\t\t\t\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t\t\t\t} else {\n\t\t\t\t\terr = c.CopyInputValuesToStash(\"email\")\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to copy email to stash: %w\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\terr = c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailRegistrationAttempted)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn c.Continue(shared.StatePasscodeConfirmation)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terr = c.CopyInputValuesToStash(\"email\")\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to copy email input to the stash: %w\", err)\n\t\t\t}\n\n\t\t\tif deps.Cfg.Email.RequireVerification {\n\t\t\t\tif err = c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailVerification); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to set passcode_template to stash: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tuserID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate a new user id: %w\", err)\n\t}\n\n\terr = c.Stash().Set(shared.StashPathUserID, userID.String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash user_id: %w\", err)\n\t}\n\n\tstates, err := a.generateRegistrationStates(c)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.Continue(append(states, shared.StateSuccess)...)\n}\n\nfunc (a RegisterLoginIdentifier) generateRegistrationStates(c flowpilot.ExecutionContext) ([]flowpilot.StateName, error) {\n\tdeps := a.GetDeps(c)\n\n\tresult := make([]flowpilot.StateName, 0)\n\n\tif deps.Cfg.Email.Enabled && deps.Cfg.Email.AcquireOnRegistration {\n\t\temailExists := len(c.Input().Get(\"email\").String()) > 0\n\t\tif emailExists && deps.Cfg.Email.RequireVerification {\n\t\t\tresult = append(result, shared.StatePasscodeConfirmation)\n\t\t}\n\t}\n\n\twebauthnAvailable := c.Stash().Get(shared.StashPathWebauthnAvailable).Bool()\n\tpasskeyEnabled := webauthnAvailable && deps.Cfg.Passkey.Enabled\n\tpasswordEnabled := deps.Cfg.Password.Enabled\n\tpasswordAndPasskeyEnabled := passkeyEnabled && passwordEnabled\n\n\talwaysAcquirePasskey := deps.Cfg.Passkey.AcquireOnRegistration == \"always\"\n\tconditionalAcquirePasskey := deps.Cfg.Passkey.AcquireOnRegistration == \"conditional\"\n\talwaysAcquirePassword := deps.Cfg.Password.AcquireOnRegistration == \"always\"\n\tconditionalAcquirePassword := deps.Cfg.Password.AcquireOnRegistration == \"conditional\"\n\tneverAcquirePasskey := deps.Cfg.Passkey.AcquireOnRegistration == \"never\"\n\tneverAcquirePassword := deps.Cfg.Password.AcquireOnRegistration == \"never\"\n\n\tif passwordAndPasskeyEnabled {\n\t\tif alwaysAcquirePasskey && alwaysAcquirePassword {\n\t\t\tif !deps.Cfg.Password.Optional && deps.Cfg.Passkey.Optional {\n\t\t\t\tresult = append(result, shared.StatePasswordCreation, shared.StateOnboardingCreatePasskey)\n\t\t\t} else {\n\t\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey, shared.StatePasswordCreation)\n\t\t\t}\n\t\t} else if alwaysAcquirePasskey && conditionalAcquirePassword {\n\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t} else if conditionalAcquirePasskey && alwaysAcquirePassword {\n\t\t\tresult = append(result, shared.StatePasswordCreation)\n\t\t} else if conditionalAcquirePasskey && conditionalAcquirePassword {\n\t\t\tresult = append(result, shared.StateCredentialOnboardingChooser)\n\t\t} else if conditionalAcquirePasskey && neverAcquirePassword {\n\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t} else if neverAcquirePasskey && (alwaysAcquirePassword || conditionalAcquirePassword) {\n\t\t\tresult = append(result, shared.StatePasswordCreation)\n\t\t} else if (alwaysAcquirePasskey || conditionalAcquirePasskey) && neverAcquirePassword {\n\t\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t\t}\n\t} else if passkeyEnabled && (alwaysAcquirePasskey || conditionalAcquirePasskey) {\n\t\tresult = append(result, shared.StateOnboardingCreatePasskey)\n\t} else if passwordEnabled && (alwaysAcquirePassword || conditionalAcquirePassword) {\n\t\tresult = append(result, shared.StatePasswordCreation)\n\t}\n\n\tif len(result) == 0 {\n\t\terr := c.ExecuteHook(shared.ScheduleMFACreationStates{})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn result, nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/registration/hook_create_user.go",
    "content": "package registration\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n\t\"github.com/tidwall/gjson\"\n)\n\ntype CreateUser struct {\n\tshared.Action\n}\n\nfunc (h CreateUser) Execute(c flowpilot.HookExecutionContext) error {\n\t// Set by shared thirdparty_oauth action because the third party callback endpoint already\n\t// creates the user.\n\tif c.Stash().Get(shared.StashPathSkipUserCreation).Bool() {\n\t\treturn nil\n\t}\n\n\tdeps := h.GetDeps(c)\n\n\tuserId, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif c.Stash().Get(shared.StashPathUserID).Exists() {\n\t\tuserId, err = uuid.FromString(c.Stash().Get(shared.StashPathUserID).String())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to parse stashed user_id into a uuid: %w\", err)\n\t\t}\n\t} else {\n\t\terr = c.Stash().Set(shared.StashPathUserID, userId.String())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set user_id to the stash: %w\", err)\n\t\t}\n\t}\n\n\terr = h.createUser(\n\t\tc,\n\t\tuserId,\n\t\tc.Stash().Get(shared.StashPathEmail).String(),\n\t\tc.Stash().Get(shared.StashPathEmailVerified).Bool(),\n\t\tc.Stash().Get(shared.StashPathUsername).String(),\n\t\tc.Stash().Get(shared.StashPathWebauthnCredentials).Array(),\n\t\tc.Stash().Get(shared.StashPathNewPassword).String(),\n\t\tc.Stash().Get(shared.StashPathOTPSecret).String(),\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create user: %w\", err)\n\t}\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserCreate, userId)\n\n\treturn nil\n}\n\nfunc (h CreateUser) createUser(c flowpilot.HookExecutionContext, id uuid.UUID, email string, emailVerified bool, username string, webauthnCredentials []gjson.Result, password, otpSecret string) error {\n\tdeps := h.GetDeps(c)\n\n\tnow := time.Now().UTC()\n\n\tvar auditLogDetails []auditlog.DetailOption\n\n\tenrolledPwd := false\n\tenrolledPasskey := false\n\tenrolledTotp := false\n\tenrolledSecurityKey := false\n\n\terr := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Create(models.User{\n\t\tID:        id,\n\t\tCreatedAt: now,\n\t\tUpdatedAt: now,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif email != \"\" {\n\t\temailModel := models.NewEmail(&id, email)\n\t\temailModel.Verified = emailVerified\n\t\terr = deps.Persister.GetEmailPersisterWithConnection(deps.Tx).Create(*emailModel)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tprimaryEmail := models.NewPrimaryEmail(emailModel.ID, id)\n\t\terr = deps.Persister.GetPrimaryEmailPersisterWithConnection(deps.Tx).Create(*primaryEmail)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, webauthnCredential := range webauthnCredentials {\n\t\tvar credentialModel models.WebauthnCredential\n\t\terr = json.Unmarshal([]byte(webauthnCredential.String()), &credentialModel)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to unmarshal stashed webauthn_credential: %w\", err)\n\t\t}\n\n\t\terr = deps.Persister.GetWebauthnCredentialPersisterWithConnection(deps.Tx).Create(credentialModel)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif credentialModel.MFAOnly {\n\t\t\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"security_key\", credentialModel.ID))\n\t\t\tenrolledSecurityKey = true\n\t\t} else {\n\t\t\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"passkey\", credentialModel.ID))\n\t\t\tenrolledPasskey = true\n\t\t}\n\t}\n\n\tif password != \"\" {\n\t\terr = deps.Persister.GetPasswordCredentialPersisterWithConnection(deps.Tx).Create(models.PasswordCredential{\n\t\t\tUserId:   id,\n\t\t\tPassword: password,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"password\", true))\n\t\tenrolledPwd = true\n\t}\n\n\tif otpSecret != \"\" {\n\t\totpSecretModel := models.NewOTPSecret(id, otpSecret)\n\t\terr = deps.Persister.GetOTPSecretPersisterWithConnection(deps.Tx).Create(*otpSecretModel)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"otp_secret\", otpSecretModel.ID))\n\t\tenrolledTotp = true\n\t}\n\n\tuser, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Get(id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif username != \"\" {\n\t\tusernameModel := models.NewUsername(user.ID, username)\n\t\terr = deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).Create(*usernameModel)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"username\", username))\n\t}\n\n\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\terr = deps.AuditLogger.Create(\n\t\tdeps.HttpContext,\n\t\tmodels.AuditLogUserCreated,\n\t\tuser,\n\t\tnil,\n\t\tauditLogDetails...,\n\t)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t}\n\n\tif err = c.Stash().Set(shared.StashPathRegistrationAMREnrolledPwd, enrolledPwd); err != nil {\n\t\treturn fmt.Errorf(\"failed to set %s to the stash: %w\", shared.StashPathRegistrationAMREnrolledPwd, err)\n\t}\n\tif err = c.Stash().Set(shared.StashPathRegistrationAMREnrolledPasskey, enrolledPasskey); err != nil {\n\t\treturn fmt.Errorf(\"failed to set %s to the stash: %w\", shared.StashPathRegistrationAMREnrolledPasskey, err)\n\t}\n\tif err = c.Stash().Set(shared.StashPathRegistrationAMREnrolledSecurityKey, enrolledSecurityKey); err != nil {\n\t\treturn fmt.Errorf(\"failed to set %s to the stash: %w\", shared.StashPathRegistrationAMREnrolledSecurityKey, err)\n\t}\n\tif err = c.Stash().Set(shared.StashPathRegistrationAMREnrolledTotp, enrolledTotp); err != nil {\n\t\treturn fmt.Errorf(\"failed to set %s to the stash: %w\", shared.StashPathRegistrationAMREnrolledTotp, err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/registration/hook_determine_amr_values.go",
    "content": "package registration\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype DetermineAMRValues struct {\n\tshared.Action\n}\n\nfunc (h DetermineAMRValues) Execute(c flowpilot.HookExecutionContext) error {\n\tif !c.IsFlow(shared.FlowRegistration) {\n\t\treturn nil\n\t}\n\n\tvar amr []string\n\n\tif c.Stash().Get(shared.StashPathRegistrationAMRUsedPasscode).Bool() {\n\t\tamr = append(amr, \"otp\")\n\t}\n\n\tif c.Stash().Get(shared.StashPathRegistrationAMRUsedThirdParty).Bool() {\n\t\tprovider := c.Stash().Get(shared.StashPathRegistrationAMRUsedThirdPartyProvider).String()\n\t\tif provider != \"\" {\n\t\t\tamr = append(amr, \"ext:\"+provider)\n\t\t} else {\n\t\t\tamr = append(amr, \"ext\")\n\t\t}\n\t}\n\n\tif c.Stash().Get(shared.StashPathRegistrationAMREnrolledPwd).Bool() {\n\t\tamr = append(amr, \"pwd\")\n\t}\n\tif c.Stash().Get(shared.StashPathRegistrationAMREnrolledPasskey).Bool() {\n\t\tamr = append(amr, \"passkey\")\n\t}\n\tif c.Stash().Get(shared.StashPathRegistrationAMREnrolledTotp).Bool() {\n\t\tamr = append(amr, \"totp\")\n\t}\n\tif c.Stash().Get(shared.StashPathRegistrationAMREnrolledSecurityKey).Bool() {\n\t\tamr = append(amr, \"security_key\")\n\t}\n\n\tif len(amr) == 0 {\n\t\treturn nil\n\t}\n\n\treturn c.Stash().Set(shared.StashPathAMRValues, amr)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/action_back.go",
    "content": "package shared\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype Back struct{}\n\nfunc (a Back) GetName() flowpilot.ActionName {\n\treturn ActionBack\n}\n\nfunc (a Back) GetDescription() string {\n\treturn \"Navigate one step back.\"\n}\n\nfunc (a Back) Initialize(c flowpilot.InitializationContext) {\n\tif !c.StateIsRevertible() {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a Back) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Revert()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/action_exchange_token.go",
    "content": "package shared\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n)\n\ntype ExchangeToken struct {\n\tAction\n}\n\nfunc (a ExchangeToken) GetName() flowpilot.ActionName {\n\treturn ActionExchangeToken\n}\n\nfunc (a ExchangeToken) GetDescription() string {\n\treturn \"Exchange a one time token.\"\n}\n\nfunc (a ExchangeToken) Initialize(c flowpilot.InitializationContext) {\n\tc.AddInputs(\n\t\tflowpilot.StringInput(\"token\").Hidden(true).Required(true),\n\t\tflowpilot.StringInput(\"code_verifier\").Hidden(true),\n\t)\n}\n\nfunc (a ExchangeToken) Execute(c flowpilot.ExecutionContext) error {\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tdeps := a.GetDeps(c)\n\n\tif deps.Cfg.RateLimiter.Enabled {\n\t\trateLimitKey := rate_limiter.CreateRateLimitTokenExchangeKey(deps.HttpContext.RealIP())\n\t\tretryAfterSeconds, ok, err := rate_limiter.Limit2(deps.TokenExchangeRateLimiter, rateLimitKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rate limiter failed: %w\", err)\n\t\t}\n\n\t\tif !ok {\n\t\t\terr = c.Payload().Set(\"retry_after\", retryAfterSeconds)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set a value for retry_after to the payload: %w\", err)\n\t\t\t}\n\t\t\treturn c.Error(ErrorRateLimitExceeded.Wrap(fmt.Errorf(\"rate limit exceeded for: %s\", rateLimitKey)))\n\t\t}\n\t}\n\n\ttokenModel, err := deps.Persister.GetTokenPersisterWithConnection(deps.Tx).GetByValue(c.Input().Get(\"token\").String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch token from db: %w\", err)\n\t}\n\n\tif tokenModel == nil {\n\t\treturn errors.New(\"token not found\")\n\t}\n\n\tif tokenModel.PKCECodeVerifier != nil && *tokenModel.PKCECodeVerifier != \"\" && *tokenModel.PKCECodeVerifier != c.Input().Get(\"code_verifier\").String() {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(errors.New(\"code_verifier does not match\")))\n\t}\n\n\tif time.Now().UTC().After(tokenModel.ExpiresAt) {\n\t\treturn errors.New(\"token expired\")\n\t}\n\n\tidentity, err := deps.Persister.GetIdentityPersisterWithConnection(deps.Tx).GetByID(*tokenModel.IdentityID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch identity from db: %w\", err)\n\t}\n\n\tuser, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Get(tokenModel.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch user from db: %w\", err)\n\t}\n\t// TODO: the identity does not need to be fetched from the DB, because it is already there in the user.\n\n\t// Set so the issue_session hook knows who to create the session for.\n\tif err := c.Stash().Set(StashPathUserID, tokenModel.UserID.String()); err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_id to stash: %w\", err)\n\t}\n\n\t// Set because the thirdparty/callback endpoint already creates a user.\n\tif err := c.Stash().Set(StashPathSkipUserCreation, true); err != nil {\n\t\treturn fmt.Errorf(\"failed to set skip_user_creation to stash: %w\", err)\n\t}\n\n\terr = deps.Persister.GetTokenPersisterWithConnection(deps.Tx).Delete(*tokenModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete token from db: %w\", err)\n\t}\n\n\tisSaml := identity.SamlIdentity != nil\n\n\tvar onboardingStates []flowpilot.StateName\n\tif isSaml {\n\t\tsamlProvider, err := deps.SamlService.GetProviderByIssuer(identity.ProviderID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not fetch saml provider for identity: %w\", err)\n\t\t}\n\t\tmustDoEmailVerification := !samlProvider.GetConfig().SkipEmailVerification && identity.Email != nil && !identity.Email.Verified\n\t\tonboardingStates, err = a.determineOnboardingStates(c, identity, user, tokenModel.UserCreated, mustDoEmailVerification)\n\t} else {\n\t\tmustDoEmailVerification := deps.Cfg.Email.RequireVerification && identity.Email != nil && !identity.Email.Verified\n\t\tonboardingStates, err = a.determineOnboardingStates(c, identity, user, tokenModel.UserCreated, mustDoEmailVerification)\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to determine onboarding states: %w\", err)\n\t}\n\n\tif err = c.Stash().Set(StashPathLoginMethod, \"third_party\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to set login_method to the stash: %w\", err)\n\t}\n\n\tif err = c.Stash().Set(StashPathThirdPartyProvider, identity.ProviderID); err != nil {\n\t\treturn fmt.Errorf(\"failed to set third_party_provider to the stash: %w\", err)\n\t}\n\n\tif c.IsFlow(FlowRegistration) {\n\t\tif err = c.Stash().Set(StashPathRegistrationAMRUsedThirdParty, true); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set %s to the stash: %w\", StashPathRegistrationAMRUsedThirdParty, err)\n\t\t}\n\t\tif err = c.Stash().Set(StashPathRegistrationAMRUsedThirdPartyProvider, identity.ProviderID); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set %s to the stash: %w\", StashPathRegistrationAMRUsedThirdPartyProvider, err)\n\t\t}\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue(onboardingStates...)\n}\n\nfunc (a ExchangeToken) determineOnboardingStates(c flowpilot.ExecutionContext, identity *models.Identity, user *models.User, userCreated bool, mustDoEmailVerification bool) ([]flowpilot.StateName, error) {\n\tdeps := a.GetDeps(c)\n\tresult := make([]flowpilot.StateName, 0)\n\n\tif mustDoEmailVerification {\n\t\tif err := c.Stash().Set(StashPathEmail, identity.Email.Address); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to stash email: %w\", err)\n\t\t}\n\n\t\tif err := c.Stash().Set(StashPathPasscodeTemplate, PasscodeTemplateEmailVerification); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to stash passcode_template: %w\", err)\n\t\t}\n\n\t\tresult = append(result, StatePasscodeConfirmation)\n\t}\n\n\tif deps.Cfg.Email.Enabled && !(len(user.Emails) > 0) {\n\t\tif (!userCreated && deps.Cfg.Email.AcquireOnLogin) ||\n\t\t\t(userCreated && deps.Cfg.Email.AcquireOnRegistration) {\n\t\t\tresult = append(result, StateOnboardingEmail)\n\t\t}\n\t}\n\n\tif deps.Cfg.Username.Enabled && user.Username == nil {\n\t\tif (!userCreated && deps.Cfg.Username.AcquireOnLogin) ||\n\t\t\t(userCreated && deps.Cfg.Username.AcquireOnRegistration) {\n\t\t\tresult = append(result, StateOnboardingUsername)\n\t\t}\n\t}\n\n\treturn append(result, StateSuccess), nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/action_skip.go",
    "content": "package shared\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype Skip struct {\n\tAction\n}\n\nfunc (a Skip) GetName() flowpilot.ActionName {\n\treturn ActionSkip\n}\n\nfunc (a Skip) GetDescription() string {\n\treturn \"Skip\"\n}\n\nfunc (a Skip) Initialize(c flowpilot.InitializationContext) {}\n\nfunc (a Skip) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/action_thirdparty_oauth.go",
    "content": "package shared\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"slices\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"golang.org/x/oauth2\"\n)\n\ntype ThirdPartyOAuth struct {\n\tAction\n}\n\nfunc (a ThirdPartyOAuth) GetName() flowpilot.ActionName {\n\treturn ActionThirdPartyOAuth\n}\n\nfunc (a ThirdPartyOAuth) GetDescription() string {\n\treturn \"Sign up/sign in with a third party provider via OAuth.\"\n}\n\nfunc (a ThirdPartyOAuth) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tenabledThirdPartyProviders := deps.Cfg.ThirdParty.Providers.GetEnabled()\n\tenabledCustomThirdPartyProviders := deps.Cfg.ThirdParty.CustomProviders.GetEnabled()\n\n\tif len(enabledCustomThirdPartyProviders) == 0 && len(enabledThirdPartyProviders) == 0 {\n\t\tc.SuspendAction()\n\t\treturn\n\t}\n\n\tproviderInput := flowpilot.StringInput(\"provider\").\n\t\tHidden(true).\n\t\tRequired(true)\n\n\tfor _, provider := range enabledThirdPartyProviders {\n\t\tproviderInput.AllowedValue(provider.DisplayName, provider.ID)\n\t}\n\tslices.SortFunc(enabledCustomThirdPartyProviders, func(a, b config.CustomThirdPartyProvider) int {\n\t\treturn cmp.Compare(a.DisplayName, b.DisplayName)\n\t})\n\n\tfor _, provider := range enabledCustomThirdPartyProviders {\n\t\tproviderInput.AllowedValue(provider.DisplayName, provider.ID)\n\t}\n\n\tc.AddInputs(\n\t\tflowpilot.StringInput(\"redirect_to\").Hidden(true).Required(true),\n\t\tproviderInput,\n\t\tflowpilot.StringInput(\"code_verifier\").Hidden(true),\n\t)\n}\n\nfunc (a ThirdPartyOAuth) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\terrorRedirectTo := deps.HttpContext.Request().Header.Get(\"Referer\")\n\tif errorRedirectTo == \"\" {\n\t\terrorRedirectTo = deps.Cfg.ThirdParty.ErrorRedirectURL\n\t}\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tredirectTo := c.Input().Get(\"redirect_to\").String()\n\tif ok := thirdparty.IsAllowedRedirect(deps.Cfg.ThirdParty, redirectTo); !ok {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tproviderName := c.Input().Get(\"provider\").String()\n\n\tprovider, err := thirdparty.GetProvider(deps.Cfg.ThirdParty, providerName)\n\tif err != nil {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid.Wrap(err))\n\t}\n\n\tcodeVerifier := c.Input().Get(\"code_verifier\")\n\tstate, err := thirdparty.GenerateState(&deps.Cfg, providerName, redirectTo, thirdparty.GenerateStateForFlowAPI(true), thirdparty.GenerateStateWithPKCECodeVerifier(codeVerifier.String()))\n\tif err != nil {\n\t\treturn c.Error(flowpilot.ErrorTechnical.Wrap(err))\n\t}\n\n\tvar opts []oauth2.AuthCodeOption\n\tif codeVerifier.Exists() {\n\t\topts = append(opts, oauth2.S256ChallengeOption(codeVerifier.String()))\n\t}\n\tauthCodeUrl := provider.AuthCodeURL(string(state), opts...)\n\n\t// cookie := utils.GenerateStateCookie(&deps.Cfg, utils.HankoThirdpartyStateCookie, string(state), utils.CookieOptions{\n\t//\tMaxAge:   300,\n\t//\tPath:     \"/\",\n\t//\tSameSite: http.SameSiteLaxMode,\n\t// })\n\n\tcookie := &http.Cookie{\n\t\tName:     utils.HankoThirdpartyStateCookie,\n\t\tValue:    string(state),\n\t\tPath:     \"/\",\n\t\tDomain:   deps.Cfg.Session.Cookie.Domain,\n\t\tMaxAge:   300,\n\t\tSecure:   true,\n\t\tHttpOnly: deps.Cfg.Session.Cookie.HttpOnly,\n\t\tSameSite: http.SameSiteNoneMode,\n\t}\n\n\tdeps.HttpContext.SetCookie(cookie)\n\n\tif err = c.Payload().Set(\"redirect_url\", authCodeUrl); err != nil {\n\t\treturn fmt.Errorf(\"failed to set redirect_url to payload: %w\", err)\n\t}\n\n\treturn c.Continue(StateThirdParty)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/const_action_names.go",
    "content": "package shared\n\nimport \"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\nconst (\n\tActionAccountDelete                          flowpilot.ActionName = \"account_delete\"\n\tActionBack                                   flowpilot.ActionName = \"back\"\n\tActionContinueToLoginOTP                     flowpilot.ActionName = \"continue_to_login_otp\"\n\tActionContinueToLoginSecurityKey             flowpilot.ActionName = \"continue_to_login_security_key\"\n\tActionContinueToOTPSecretCreation            flowpilot.ActionName = \"continue_to_otp_secret_creation\"\n\tActionContinueToPasscodeConfirmation         flowpilot.ActionName = \"continue_to_passcode_confirmation\"\n\tActionContinueToPasscodeConfirmationRecovery flowpilot.ActionName = \"continue_to_passcode_confirmation_recovery\"\n\tActionContinueToPasskeyRegistration          flowpilot.ActionName = \"continue_to_passkey_registration\"\n\tActionContinueToPasswordLogin                flowpilot.ActionName = \"continue_to_password_login\"\n\tActionContinueToPasswordRegistration         flowpilot.ActionName = \"continue_to_password_registration\"\n\tActionContinueToSecurityKeyCreation          flowpilot.ActionName = \"continue_to_security_key_creation\"\n\tActionContinueWithLoginIdentifier            flowpilot.ActionName = \"continue_with_login_identifier\"\n\tActionEmailAddressSet                        flowpilot.ActionName = \"email_address_set\"\n\tActionEmailCreate                            flowpilot.ActionName = \"email_create\"\n\tActionEmailDelete                            flowpilot.ActionName = \"email_delete\"\n\tActionEmailSetPrimary                        flowpilot.ActionName = \"email_set_primary\"\n\tActionEmailVerify                            flowpilot.ActionName = \"email_verify\"\n\tActionExchangeToken                          flowpilot.ActionName = \"exchange_token\"\n\tActionOTPCodeValidate                        flowpilot.ActionName = \"otp_code_validate\"\n\tActionOTPCodeVerify                          flowpilot.ActionName = \"otp_code_verify\"\n\tActionOTPSecretDelete                        flowpilot.ActionName = \"otp_secret_delete\"\n\tActionPasswordCreate                         flowpilot.ActionName = \"password_create\"\n\tActionPasswordDelete                         flowpilot.ActionName = \"password_delete\"\n\tActionPasswordLogin                          flowpilot.ActionName = \"password_login\"\n\tActionPasswordRecovery                       flowpilot.ActionName = \"password_recovery\"\n\tActionPasswordUpdate                         flowpilot.ActionName = \"password_update\"\n\tActionPatchMetadata                          flowpilot.ActionName = \"patch_metadata\"\n\tActionRegisterClientCapabilities             flowpilot.ActionName = \"register_client_capabilities\"\n\tActionRegisterLoginIdentifier                flowpilot.ActionName = \"register_login_identifier\"\n\tActionRegisterPassword                       flowpilot.ActionName = \"register_password\"\n\tActionRememberMe                             flowpilot.ActionName = \"remember_me\"\n\tActionResendPasscode                         flowpilot.ActionName = \"resend_passcode\"\n\tActionSecurityKeyCreate                      flowpilot.ActionName = \"security_key_create\"\n\tActionSecurityKeyDelete                      flowpilot.ActionName = \"security_key_delete\"\n\tActionSkip                                   flowpilot.ActionName = \"skip\"\n\tActionThirdPartyOAuth                        flowpilot.ActionName = \"thirdparty_oauth\"\n\tActionTrustDevice                            flowpilot.ActionName = \"trust_device\"\n\tActionUsernameCreate                         flowpilot.ActionName = \"username_create\"\n\tActionUsernameDelete                         flowpilot.ActionName = \"username_delete\"\n\tActionUsernameUpdate                         flowpilot.ActionName = \"username_update\"\n\tActionVerifyPasscode                         flowpilot.ActionName = \"verify_passcode\"\n\tActionWebauthnCredentialCreate               flowpilot.ActionName = \"webauthn_credential_create\"\n\tActionWebauthnCredentialDelete               flowpilot.ActionName = \"webauthn_credential_delete\"\n\tActionWebauthnCredentialRename               flowpilot.ActionName = \"webauthn_credential_rename\"\n\tActionWebauthnGenerateCreationOptions        flowpilot.ActionName = \"webauthn_generate_creation_options\"\n\tActionWebauthnGenerateRequestOptions         flowpilot.ActionName = \"webauthn_generate_request_options\"\n\tActionWebauthnVerifyAssertionResponse        flowpilot.ActionName = \"webauthn_verify_assertion_response\"\n\tActionWebauthnVerifyAttestationResponse      flowpilot.ActionName = \"webauthn_verify_attestation_response\"\n\tActionSessionDelete                          flowpilot.ActionName = \"session_delete\"\n\tActionConnectThirdpartyOauthProvider         flowpilot.ActionName = \"connect_thirdparty_oauth_provider\"\n\tActionDisconnectThirdpartyOauthProvider      flowpilot.ActionName = \"disconnect_thirdparty_oauth_provider\"\n)\n"
  },
  {
    "path": "backend/flow_api/flow/shared/const_email_templates.go",
    "content": "package shared\n\nconst (\n\tPasscodeTemplateLogin                      = \"login\"\n\tPasscodeTemplateRecovery                   = \"recovery\"\n\tPasscodeTemplateEmailVerification          = \"email_verification\"\n\tPasscodeTemplateEmailLoginAttempted        = \"email_login_attempted\"\n\tPasscodeTemplateEmailRegistrationAttempted = \"email_registration_attempted\"\n)\n"
  },
  {
    "path": "backend/flow_api/flow/shared/const_flow_names.go",
    "content": "package shared\n\nimport \"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\nconst (\n\tFlowCapabilities         flowpilot.FlowName = \"capabilities\"\n\tFlowCredentialOnboarding flowpilot.FlowName = \"credential_onboarding\"\n\tFlowCredentialUsage      flowpilot.FlowName = \"credential_usage\"\n\tFlowDeviceTrust          flowpilot.FlowName = \"device_trust\"\n\tFlowTokenExchange        flowpilot.FlowName = \"token_exchange\"\n\tFlowLogin                flowpilot.FlowName = \"login\"\n\tFlowMFACreation          flowpilot.FlowName = \"mfa_creation\"\n\tFlowProfile              flowpilot.FlowName = \"profile\"\n\tFlowRegistration         flowpilot.FlowName = \"registration\"\n\tFlowUserDetails          flowpilot.FlowName = \"user_details\"\n\tFlowMFAUsage             flowpilot.FlowName = \"mfa_usage\"\n)\n"
  },
  {
    "path": "backend/flow_api/flow/shared/const_stash_paths.go",
    "content": "package shared\n\nconst (\n\tStashPathAMRValues = \"amr\"\n\n\t// Registration-only keys for AMR derivation\n\tStashPathRegistrationAMRUsedPasscode           = \"registration.amr.passcode\"\n\tStashPathRegistrationAMRUsedThirdParty         = \"registration.amr.used.third_party\"\n\tStashPathRegistrationAMRUsedThirdPartyProvider = \"registration.amr.used.third_party_provider\"\n\tStashPathRegistrationAMREnrolledPwd            = \"registration.amr.enrolled.pwd\"\n\tStashPathRegistrationAMREnrolledPasskey        = \"registration.amr.enrolled.passkey\"\n\tStashPathRegistrationAMREnrolledTotp           = \"registration.amr.enrolled.totp\"\n\tStashPathRegistrationAMREnrolledSecurityKey    = \"registration.amr.enrolled.security_key\"\n\n\tStashPathDeviceTrustGranted                     = \"device_trust_granted\"\n\tStashPathEmail                                  = \"email\"\n\tStashPathEmailVerified                          = \"email_verified\"\n\tStashPathLoginMethod                            = \"login_method\"\n\tStashPathLoginOnboardingCreateEmail             = \"login_onboarding_create_email\"\n\tStashPathLoginOnboardingScheduled               = \"login_onboarding_scheduled\"\n\tStashPathMFAUsageMethod                         = \"mfa_method\"\n\tStashPathCreateMFAOnlyCredential                = \"create_mfa_only_credential\"\n\tStashPathNewPassword                            = \"new_password\"\n\tStashPathOTPSecret                              = \"otp_secret\"\n\tStashPathOTPImageSource                         = \"otp_image_src\"\n\tStashPathPasscodeEmail                          = \"sticky.passcode_email\"\n\tStashPathPasscodeID                             = \"sticky.passcode_id\"\n\tStashPathPasscodeTemplate                       = \"passcode_template\"\n\tStashPathPasswordRecoveryPending                = \"pw_recovery_pending\"\n\tStashPathRememberMeSelected                     = \"remember_me_selected\"\n\tStashPathSecurityKeyAttachmentSupported         = \"security_key_attachment_supported\"\n\tStashPathSkipUserCreation                       = \"skip_user_creation\"\n\tStashPathThirdPartyProvider                     = \"third_party_provider\"\n\tStashPathUserHasEmails                          = \"user_has_emails\"\n\tStashPathUserHasOTPSecret                       = \"user_hat_otp_secret\"\n\tStashPathUserHasPasskey                         = \"user_has_passkey\"\n\tStashPathUserHasPassword                        = \"user_has_password\"\n\tStashPathUserHasSecurityKey                     = \"user_has_security_key\"\n\tStashPathUserHasUsername                        = \"user_has_username\"\n\tStashPathUserHasWebauthnCredential              = \"user_has_webauthn_credential\"\n\tStashPathUserID                                 = \"user_id\"\n\tStashPathUserIdentification                     = \"user_identification\"\n\tStashPathUsername                               = \"username\"\n\tStashPathWebauthnAvailable                      = \"webauthn_available\"\n\tStashPathWebauthnConditionalMediationAvailable  = \"webauthn_conditional_mediation_available\"\n\tStashPathWebauthnCredentials                    = \"webauthn_credentials\"\n\tStashPathWebauthnPlatformAuthenticatorAvailable = \"webauthn_platform_authenticator_available\"\n\tStashPathWebauthnSessionDataID                  = \"webauthn_session_data_id\"\n)\n"
  },
  {
    "path": "backend/flow_api/flow/shared/const_state_names.go",
    "content": "package shared\n\nimport \"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\nconst (\n\tStateCredentialOnboardingChooser           flowpilot.StateName = \"credential_onboarding_chooser\"\n\tStateDeviceTrust                           flowpilot.StateName = \"device_trust\"\n\tStateError                                 flowpilot.StateName = \"error\"\n\tStateLoginInit                             flowpilot.StateName = \"login_init\"\n\tStateLoginMethodChooser                    flowpilot.StateName = \"login_method_chooser\"\n\tStateLoginOTP                              flowpilot.StateName = \"login_otp\"\n\tStateLoginPasskey                          flowpilot.StateName = \"login_passkey\"\n\tStateLoginPassword                         flowpilot.StateName = \"login_password\"\n\tStateLoginPasswordRecovery                 flowpilot.StateName = \"login_password_recovery\"\n\tStateLoginSecurityKey                      flowpilot.StateName = \"login_security_key\"\n\tStateMFAMethodChooser                      flowpilot.StateName = \"mfa_method_chooser\"\n\tStateOnboardingCreatePasskey               flowpilot.StateName = \"onboarding_create_passkey\"\n\tStateOnboardingEmail                       flowpilot.StateName = \"onboarding_email\"\n\tStateOnboardingUsername                    flowpilot.StateName = \"onboarding_username\"\n\tStateOnboardingVerifyPasskeyAttestation    flowpilot.StateName = \"onboarding_verify_passkey_attestation\"\n\tStateMFAOTPSecretCreation                  flowpilot.StateName = \"mfa_otp_secret_creation\"\n\tStatePasscodeConfirmation                  flowpilot.StateName = \"passcode_confirmation\"\n\tStatePasswordCreation                      flowpilot.StateName = \"password_creation\"\n\tStatePreflight                             flowpilot.StateName = \"preflight\"\n\tStateProfileAccountDeleted                 flowpilot.StateName = \"account_deleted\"\n\tStateProfileInit                           flowpilot.StateName = \"profile_init\"\n\tStateProfileWebauthnCredentialVerification flowpilot.StateName = \"webauthn_credential_verification\"\n\tStateRegistrationInit                      flowpilot.StateName = \"registration_init\"\n\tStateMFASecurityKeyCreation                flowpilot.StateName = \"mfa_security_key_creation\"\n\tStateSuccess                               flowpilot.StateName = \"success\"\n\tStateThirdParty                            flowpilot.StateName = \"thirdparty\"\n)\n"
  },
  {
    "path": "backend/flow_api/flow/shared/errors.go",
    "content": "package shared\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"net/http\"\n)\n\nvar (\n\tErrorNotFound                         = flowpilot.NewFlowError(\"not_found\", \"The requested resource was not found.\", http.StatusNotFound)\n\tErrorPasscodeMaxAttemptsReached       = flowpilot.NewFlowError(\"passcode_max_attempts_reached\", \"The passcode was entered wrong too many times.\", http.StatusUnauthorized)\n\tErrorPasskeyInvalid                   = flowpilot.NewFlowError(\"passkey_invalid\", \"The passkey is invalid.\", http.StatusUnauthorized)\n\tErrorRateLimitExceeded                = flowpilot.NewFlowError(\"rate_limit_exceeded\", \"The rate limit has been exceeded.\", http.StatusTooManyRequests)\n\tErrorUnauthorized                     = flowpilot.NewFlowError(\"unauthorized\", \"The session is invalid.\", http.StatusUnauthorized)\n\tErrorWebauthnCredentialInvalidMFAOnly = flowpilot.NewFlowError(\"webauthn_credential_invalid_mfa_only\", \"This credential can be used as a second factor security key only.\", http.StatusUnauthorized)\n\tErrorPlatformAuthenticatorRequired    = flowpilot.NewFlowError(\"platform_authenticator_required\", \"The device or browser does not support the required platform authenticators.\", http.StatusUnauthorized)\n)\n\nvar (\n\tErrorEmailAlreadyExists    = flowpilot.NewInputError(\"email_already_exists\", \"The email address already exists.\")\n\tErrorUsernameAlreadyExists = flowpilot.NewInputError(\"username_already_exists\", \"The username already exists.\")\n\tErrorUnknownUsername       = flowpilot.NewInputError(\"unknown_username_error\", \"The username is unknown.\")\n\tErrorUnknownEmail          = flowpilot.NewInputError(\"unknown_email_error\", \"The email address is unknown.\")\n\tErrorInvalidUsername       = flowpilot.NewInputError(\"invalid_username_error\", \"The username is invalid.\")\n\tErrorPasscodeInvalid       = flowpilot.NewInputError(\"passcode_invalid\", \"The passcode is invalid.\")\n\tErrorInvalidMetadata       = flowpilot.NewInputError(\"invalid_metadata_error\", \"The metadata is invalid.\")\n)\n"
  },
  {
    "path": "backend/flow_api/flow/shared/flow.go",
    "content": "package shared\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/sethvargo/go-limiter\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\ntype Dependencies struct {\n\tCfg                         config.Config\n\tHttpContext                 echo.Context\n\tSecurityNotificationService services.SecurityNotification\n\tPasscodeService             services.Passcode\n\tPasswordService             services.Password\n\tWebauthnService             services.WebauthnService\n\tSamlService                 saml.Service\n\tPersister                   persistence.Persister\n\tSessionManager              session.Manager\n\tOTPRateLimiter              limiter.Store\n\tPasscodeRateLimiter         limiter.Store\n\tPasswordRateLimiter         limiter.Store\n\tTokenExchangeRateLimiter    limiter.Store\n\tTx                          *pop.Connection\n\tAuthenticatorMetadata       mapper.AuthenticatorMetadata\n\tAuditLogger                 auditlog.Logger\n}\n\ntype Action struct{}\n\nfunc (a *Action) GetDeps(c flowpilot.Context) *Dependencies {\n\treturn c.Get(\"deps\").(*Dependencies)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_determine_amr_values.go",
    "content": "package shared\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype DetermineAMRValues struct {\n\tAction\n}\n\nfunc (h DetermineAMRValues) Execute(c flowpilot.HookExecutionContext) error {\n\tloginMethod := c.Stash().Get(StashPathLoginMethod).String()\n\tmfaMethod := c.Stash().Get(StashPathMFAUsageMethod).String()\n\tthirdPartyProvider := c.Stash().Get(StashPathThirdPartyProvider).String()\n\n\tvar amr []string\n\n\tswitch loginMethod {\n\tcase \"password\":\n\t\tamr = append(amr, \"pwd\")\n\tcase \"passkey\":\n\t\tamr = append(amr, \"passkey\")\n\tcase \"passcode\":\n\t\tamr = append(amr, \"otp\")\n\tcase \"third_party\":\n\t\tif thirdPartyProvider != \"\" {\n\t\t\tamr = append(amr, \"ext:\"+thirdPartyProvider)\n\t\t} else {\n\t\t\tamr = append(amr, \"ext\")\n\t\t}\n\t}\n\n\tswitch mfaMethod {\n\tcase \"totp\":\n\t\tamr = append(amr, \"totp\")\n\tcase \"security_key\":\n\t\tamr = append(amr, \"security_key\")\n\t}\n\n\tif len(amr) == 0 {\n\t\treturn nil\n\t}\n\n\treturn c.Stash().Set(StashPathAMRValues, amr)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_email_persist_verified_status.go",
    "content": "package shared\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype EmailPersistVerifiedStatus struct {\n\tAction\n}\n\nfunc (h EmailPersistVerifiedStatus) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif !c.Stash().Get(StashPathEmailVerified).Bool() {\n\t\treturn nil\n\t}\n\n\tif !c.Stash().Get(StashPathEmail).Exists() {\n\t\treturn errors.New(\"verified email not set on the stash\")\n\t}\n\n\tif !c.Stash().Get(StashPathUserID).Exists() {\n\t\treturn errors.New(\"user_id not set on the stash\")\n\t}\n\n\tuserId, err := uuid.FromString(c.Stash().Get(StashPathUserID).String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse stashed user_id into a uuid: %w\", err)\n\t}\n\n\tuser, err := deps.Persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user by user_id: %w\", err)\n\t}\n\n\temailAddressToVerify := c.Stash().Get(StashPathEmail).String()\n\n\temailAddressToVerifyModel, err := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).FindByAddress(emailAddressToVerify)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not fetch email: %w\", err)\n\t}\n\n\tvar emailCreated bool\n\tif emailAddressToVerifyModel == nil {\n\t\tnewEmailModel := models.NewEmail(&userId, emailAddressToVerify)\n\t\tnewEmailModel.Verified = true\n\n\t\terr := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).Create(*newEmailModel)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not save email: %w\", err)\n\t\t}\n\n\t\temailModels, err := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).FindByUserId(*newEmailModel.UserID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could fetch emails: %w\", err)\n\t\t}\n\n\t\tif userModel, ok := c.Get(\"session_user\").(*models.User); ok {\n\t\t\tuserModel.Emails = append(userModel.Emails, *newEmailModel)\n\t\t}\n\n\t\tif len(emailModels) == 1 && emailModels[0].ID.String() == newEmailModel.ID.String() {\n\t\t\t// The user has only one 1 email and it is the email we just added. It makes sense then,\n\t\t\t// to automatically set this as the primary email.\n\t\t\tprimaryEmailModel := models.NewPrimaryEmail(newEmailModel.ID, userId)\n\t\t\terr = deps.Persister.GetPrimaryEmailPersisterWithConnection(deps.Tx).Create(*primaryEmailModel)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not save primary email: %w\", err)\n\t\t\t}\n\n\t\t\tif userModel, ok := c.Get(\"session_user\").(*models.User); ok {\n\t\t\t\tuserModel.SetPrimaryEmail(primaryEmailModel)\n\t\t\t}\n\t\t}\n\n\t\temailCreated = true\n\t} else if !emailAddressToVerifyModel.Verified {\n\t\temailAddressToVerifyModel.Verified = true\n\t\terr = deps.Persister.GetEmailPersisterWithConnection(deps.Tx).Update(*emailAddressToVerifyModel)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not update email: %w\", err)\n\t\t}\n\n\t\tif userModel, ok := c.Get(\"session_user\").(*models.User); ok {\n\t\t\tuserModel.UpdateEmail(*emailAddressToVerifyModel)\n\t\t}\n\t}\n\n\t// Audit log verification only if this is not a login via passcode because it implies verification.\n\t// Only login actions should set the \"login_method\" stash entry.\n\tif c.Stash().Get(StashPathLoginMethod).String() != \"passcode\" {\n\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\tdeps.Tx,\n\t\t\tdeps.HttpContext,\n\t\t\tmodels.AuditLogEmailVerified,\n\t\t\t&models.User{ID: userId},\n\t\t\tnil,\n\t\t\tauditlog.Detail(\"email\", emailAddressToVerify),\n\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t}\n\t}\n\n\tif emailCreated {\n\t\tif deps.Cfg.SecurityNotifications.Notifications.EmailCreate.Enabled {\n\t\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\t\tEmailAddress: user.Emails.GetPrimary().Address,\n\t\t\t\tTemplate:     \"email_create\",\n\t\t\t\tHttpContext:  deps.HttpContext,\n\t\t\t\tBodyData: map[string]interface{}{\n\t\t\t\t\t\"NewEmailAddress\": emailAddressToVerify,\n\t\t\t\t},\n\t\t\t\tData: &webhook.SecurityNotificationData{\n\t\t\t\t\tNewEmailAddress: emailAddressToVerify,\n\t\t\t\t},\n\t\t\t\tUserContext: *user,\n\t\t\t})\n\t\t}\n\n\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\tdeps.Tx,\n\t\t\tdeps.HttpContext,\n\t\t\tmodels.AuditLogEmailCreated,\n\t\t\t&models.User{ID: userId},\n\t\t\tnil,\n\t\t\tauditlog.Detail(\"email\", emailAddressToVerify),\n\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()))\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t}\n\n\t\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserEmailCreate, userId)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_generate_oauth_links.go",
    "content": "package shared\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype GenerateOAuthLinks struct {\n\tAction\n}\n\nfunc (h GenerateOAuthLinks) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\treturnToUrl := deps.Cfg.ThirdParty.DefaultRedirectURL\n\n\treferer := deps.HttpContext.Request().Header.Get(\"Referer\")\n\tif referer != \"\" {\n\t\tu, err := url.Parse(referer)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// remove any query and fragment parts of the referer\n\t\tu.RawQuery = \"\"\n\t\tu.Fragment = \"\"\n\t\treturnToUrl = u.String()\n\t}\n\n\tif deps.Cfg.ThirdParty.Providers.GitHub.Enabled {\n\t\tc.AddLink(OAuthLink(\"github\", h.generateHref(deps.HttpContext, \"github\", returnToUrl)))\n\t}\n\tif deps.Cfg.ThirdParty.Providers.Google.Enabled {\n\t\tc.AddLink(OAuthLink(\"google\", h.generateHref(deps.HttpContext, \"google\", returnToUrl)))\n\t}\n\tif deps.Cfg.ThirdParty.Providers.Apple.Enabled {\n\t\tc.AddLink(OAuthLink(\"apple\", h.generateHref(deps.HttpContext, \"apple\", returnToUrl)))\n\t}\n\tif deps.Cfg.ThirdParty.Providers.Facebook.Enabled {\n\t\tc.AddLink(OAuthLink(\"facebook\", h.generateHref(deps.HttpContext, \"facebook\", returnToUrl)))\n\t}\n\n\treturn nil\n}\n\nfunc (h GenerateOAuthLinks) generateHref(c echo.Context, provider string, returnToUrl string) string {\n\thost := c.Request().Host\n\tforwardedProto := c.Request().Header.Get(\"X-Forwarded-Proto\")\n\tif forwardedProto == \"\" {\n\t\t// Assume that a proxy is setting the X-Forwarded-Proto header correctly. Hanko should always be deployed behind a proxy,\n\t\t// because you cannot start the backend with https and passkeys only work in a secure context.\n\t\t// If the X-Forwarded-Proto header is not set, set it to 'http' because otherwise you would need to set up a https environment for local testing.\n\t\tforwardedProto = \"http\"\n\t}\n\n\tu, _ := url.Parse(fmt.Sprintf(\"%s://%s/thirdparty/auth\", forwardedProto, host))\n\tquery := url.Values{}\n\tquery.Set(\"provider\", provider)\n\tif returnToUrl != \"\" {\n\t\tquery.Set(\"redirect_to\", returnToUrl)\n\t}\n\tu.RawQuery = query.Encode()\n\n\treturn u.String()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_get_user_data.go",
    "content": "package shared\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype GetUserData struct {\n\tAction\n}\n\nfunc (h GetUserData) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tuserId, err := uuid.FromString(c.Stash().Get(\"user_id\").String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse stashed user_id into a uuid: %w\", err)\n\t}\n\n\tuserModel, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user from db: %w\", err)\n\t}\n\n\terr = c.Payload().Set(\"user\", dto.ProfileDataFromUserModel(userModel, &deps.Cfg))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user payload: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_issue_session.go",
    "content": "package shared\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\ntype IssueSession struct {\n\tAction\n}\n\nfunc (h IssueSession) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tvar userId uuid.UUID\n\tvar err error\n\tif c.Stash().Get(StashPathUserID).Exists() {\n\t\tuserId, err = uuid.FromString(c.Stash().Get(StashPathUserID).String())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to parse stashed user_id into a uuid: %w\", err)\n\t\t}\n\t} else {\n\t\treturn errors.New(\"user_id not found in stash\")\n\t}\n\n\tuserModel, err := deps.Persister.GetUserPersisterWithConnection(deps.Tx).Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch user from db: %w\", err)\n\t}\n\n\tuserJWT := dto.UserJWTFromUserModel(userModel)\n\n\tvar jwtOpts []session.JWTOptions\n\tif c.Stash().Get(StashPathAMRValues).Exists() {\n\t\tvar amr []string\n\t\tfor _, v := range c.Stash().Get(StashPathAMRValues).Array() {\n\t\t\tif s := v.String(); s != \"\" {\n\t\t\t\tamr = append(amr, s)\n\t\t\t}\n\t\t}\n\t\tif len(amr) > 0 {\n\t\t\tjwtOpts = append(jwtOpts, session.WithValue(\"amr\", amr))\n\t\t}\n\t}\n\n\tsignedSessionToken, rawToken, err := deps.SessionManager.GenerateJWT(userJWT, jwtOpts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate JWT: %w\", err)\n\t}\n\n\tclaims, err := dto.GetClaimsFromToken(rawToken)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get token claims: %w\", err)\n\t}\n\n\terr = c.Payload().Set(\"claims\", claims)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set token claims to payload: %w\", err)\n\t}\n\n\tactiveSessions, err := deps.Persister.GetSessionPersisterWithConnection(deps.Tx).ListActive(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list active sessions: %w\", err)\n\t}\n\n\t// remove all server side sessions that exceed the limit\n\tif len(activeSessions) >= deps.Cfg.Session.Limit {\n\t\tfor i := deps.Cfg.Session.Limit - 1; i < len(activeSessions); i++ {\n\t\t\terr = deps.Persister.GetSessionPersisterWithConnection(deps.Tx).Delete(activeSessions[i])\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to remove latest session: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tsessionID, _ := rawToken.Get(\"session_id\")\n\n\texpirationTime := rawToken.Expiration()\n\tsessionModel := models.Session{\n\t\tID:        uuid.FromStringOrNil(sessionID.(string)),\n\t\tUserID:    userId,\n\t\tCreatedAt: rawToken.IssuedAt(),\n\t\tUpdatedAt: rawToken.IssuedAt(),\n\t\tExpiresAt: &expirationTime,\n\t\tLastUsed:  rawToken.IssuedAt(),\n\t}\n\n\tif deps.Cfg.Session.AcquireIPAddress {\n\t\tsessionModel.IpAddress = nulls.NewString(deps.HttpContext.RealIP())\n\t}\n\n\tif deps.Cfg.Session.AcquireUserAgent {\n\t\tsessionModel.UserAgent = nulls.NewString(deps.HttpContext.Request().UserAgent())\n\t}\n\n\terr = deps.Persister.GetSessionPersisterWithConnection(deps.Tx).Create(sessionModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store session: %w\", err)\n\t}\n\n\trememberMeSelected := c.Stash().Get(StashPathRememberMeSelected).Bool()\n\tcookie, err := deps.SessionManager.GenerateCookie(signedSessionToken)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate auth cookie, %w\", err)\n\t}\n\n\tlifespan, err := time.ParseDuration(deps.Cfg.Session.Lifespan)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse session lifespan: %w\", err)\n\t}\n\n\tsessionRetention := \"persistent\"\n\tif deps.Cfg.Session.Cookie.Retention == \"session\" ||\n\t\t(deps.Cfg.Session.Cookie.Retention == \"prompt\" && !rememberMeSelected) {\n\t\t// Issue a session cookie.\n\t\tcookie.MaxAge = 0\n\t\tsessionRetention = \"session\"\n\t}\n\n\tdeps.HttpContext.Response().Header().Set(\"X-Session-Lifetime\", fmt.Sprintf(\"%d\", int(lifespan.Seconds())))\n\tdeps.HttpContext.Response().Header().Set(\"X-Session-Retention\", fmt.Sprintf(\"%s\", sessionRetention))\n\n\tif deps.Cfg.Session.EnableAuthTokenHeader {\n\t\tdeps.HttpContext.Response().Header().Set(\"X-Auth-Token\", signedSessionToken)\n\t} else {\n\t\tdeps.HttpContext.SetCookie(cookie)\n\t}\n\n\tloginMethod := c.Stash().Get(StashPathLoginMethod)\n\tmfaMethod := c.Stash().Get(StashPathMFAUsageMethod)\n\tthirdPartyProvider := c.Stash().Get(StashPathThirdPartyProvider)\n\n\t// Audit log logins (including third party signups) only, because user creation on registration implies that the\n\t// user is logged in after a registration. Only login actions should set the \"login_method\" stash entry.\n\tif c.IsFlow(FlowLogin) || c.IsFlow(FlowTokenExchange) && loginMethod.Exists() {\n\t\tauditLogDetails := []auditlog.DetailOption{\n\t\t\tauditlog.Detail(\"login_method\", loginMethod.String()),\n\t\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()),\n\t\t}\n\n\t\tif mfaMethod.Exists() {\n\t\t\tauditLogDetails = append(\n\t\t\t\tauditLogDetails,\n\t\t\t\tauditlog.Detail(\"mfa_method\", mfaMethod.String()),\n\t\t\t)\n\t\t}\n\n\t\terr = deps.AuditLogger.CreateWithConnection(\n\t\t\tdeps.Tx,\n\t\t\tdeps.HttpContext,\n\t\t\tmodels.AuditLogLoginSuccess,\n\t\t\t&models.User{ID: userId},\n\t\t\terr,\n\t\t\tauditLogDetails...)\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t\t}\n\t}\n\n\tif c.IsFlow(FlowLogin) || c.IsFlow(FlowTokenExchange) && loginMethod.Exists() {\n\t\tif err := c.Payload().Set(\"last_login.login_method\", loginMethod.String()); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set login_method to the payload: %w\", err)\n\t\t}\n\n\t\tif thirdPartyProvider.Exists() {\n\t\t\tif err := c.Payload().Set(\"last_login.third_party_provider\", thirdPartyProvider.String()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set third_party_provider to the payload: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tif mfaMethod.Exists() {\n\t\t\tif err := c.Payload().Set(\"last_login.mfa_method\", mfaMethod.String()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set mfa_method to the payload: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_password_save.go",
    "content": "package shared\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype PasswordSave struct {\n\tAction\n}\n\nfunc (h PasswordSave) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif !c.Stash().Get(StashPathNewPassword).Exists() {\n\t\treturn nil\n\t}\n\n\tpasswordId, _ := uuid.NewV4()\n\tpasswordCredential := models.PasswordCredential{\n\t\tID:       passwordId,\n\t\tUserId:   uuid.FromStringOrNil(c.Stash().Get(StashPathUserID).String()),\n\t\tPassword: c.Stash().Get(StashPathNewPassword).String(),\n\t}\n\n\terr := deps.Persister.GetPasswordCredentialPersisterWithConnection(deps.Tx).Create(passwordCredential)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create password: %w\", err)\n\t}\n\t// TODO: add audit log?\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_persist_webauthn_credential.go",
    "content": "package shared\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnCredentialSave struct {\n\tAction\n}\n\nfunc (h WebauthnCredentialSave) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif !c.Stash().Get(StashPathUserID).Exists() {\n\t\treturn nil\n\t}\n\n\tuserId, err := uuid.FromString(c.Stash().Get(StashPathUserID).String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse stashed user_id into a uuid: %w\", err)\n\t}\n\n\tif !c.Stash().Get(StashPathWebauthnCredentials).Exists() {\n\t\treturn nil\n\t}\n\n\tauditLogDetails := []auditlog.DetailOption{\n\t\tauditlog.Detail(\"flow_id\", c.GetFlowID()),\n\t}\n\n\tauditLogType := models.AuditLogPasskeyCreated\n\n\tfor _, webauthnCredential := range c.Stash().Get(StashPathWebauthnCredentials).Array() {\n\n\t\tvar credentialModel models.WebauthnCredential\n\t\terr = json.Unmarshal([]byte(webauthnCredential.String()), &credentialModel)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to unmarshal stashed webauthn_credential: %w\", err)\n\t\t}\n\n\t\terr = deps.Persister.GetWebauthnCredentialPersisterWithConnection(deps.Tx).Create(credentialModel)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar userModel *models.User\n\t\tif userValue, ok := c.Get(\"session_user\").(*models.User); ok {\n\t\t\tuserModel = userValue\n\t\t\tuserModel.WebauthnCredentials = append(userModel.WebauthnCredentials, credentialModel)\n\t\t}\n\n\t\tvar isPasskey bool = false\n\n\t\tif credentialModel.MFAOnly {\n\t\t\tauditLogType = models.AuditLogSecurityKeyCreated\n\t\t\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"security_key\", credentialModel.ID))\n\t\t} else {\n\t\t\tisPasskey = true\n\t\t\tauditLogDetails = append(auditLogDetails, auditlog.Detail(\"passkey\", credentialModel.ID))\n\t\t}\n\n\t\tif userModel != nil {\n\t\t\temailAddress := userModel.Emails.GetPrimary().Address\n\n\t\t\tif !isPasskey {\n\t\t\t\t// Send user an email informing of new MFA method\n\t\t\t\tif deps.Cfg.SecurityNotifications.Notifications.MFACreate.Enabled {\n\t\t\t\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\t\t\t\tEmailAddress: emailAddress,\n\t\t\t\t\t\tTemplate:     \"mfa_create\",\n\t\t\t\t\t\tHttpContext:  deps.HttpContext,\n\t\t\t\t\t\tUserContext:  *userModel,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif isPasskey && deps.Cfg.SecurityNotifications.Notifications.PasskeyCreate.Enabled {\n\t\t\t\tdeps.SecurityNotificationService.SendNotification(deps.Tx, services.SendSecurityNotificationParams{\n\t\t\t\t\tEmailAddress: emailAddress,\n\t\t\t\t\tTemplate:     \"passkey_create\",\n\t\t\t\t\tHttpContext:  deps.HttpContext,\n\t\t\t\t\tUserContext:  *userModel,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\terr = c.Stash().Delete(StashPathWebauthnCredentials)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = deps.AuditLogger.CreateWithConnection(\n\t\tdeps.Tx,\n\t\tdeps.HttpContext,\n\t\tauditLogType,\n\t\t&models.User{ID: userId},\n\t\tnil,\n\t\tauditLogDetails...)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_schedule_mfa_creation_states.go",
    "content": "package shared\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype ScheduleMFACreationStates struct {\n\tAction\n}\n\nfunc (h ScheduleMFACreationStates) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif c.IsStateScheduled(StatePasswordCreation) ||\n\t\tc.Stash().Get(StashPathPasswordRecoveryPending).Bool() {\n\t\t// Delay MFA onboarding until a password has eventually been set or updated.\n\t\treturn nil\n\t}\n\n\tif c.StateVisited(StateMFAMethodChooser) {\n\t\t// Show MFA onboarding only once within a flow unless states have been reverted.\n\t\treturn nil\n\t}\n\n\tmfaConfig := deps.Cfg.MFA\n\tpasswordsEnabled := deps.Cfg.Password.Enabled\n\tpasscodeEmailsEnabled := deps.Cfg.Email.Enabled && deps.Cfg.Email.UseForAuthentication\n\tuserHasEmail := c.Stash().Get(StashPathEmail).Exists() || c.Stash().Get(StashPathUserHasEmails).Bool()\n\tuserHasPassword := c.Stash().Get(StashPathUserHasPassword).Bool()\n\tmfaLoginEnabled := (passwordsEnabled && userHasPassword) || (passcodeEmailsEnabled && userHasEmail)\n\tmfaAcquireConfigured := mfaConfig.Enabled &&\n\t\t(mfaConfig.SecurityKeys.Enabled || mfaConfig.TOTP.Enabled) &&\n\t\t((c.GetFlowName() == FlowLogin && mfaConfig.AcquireOnLogin) ||\n\t\t\t(c.GetFlowName() == FlowRegistration && mfaConfig.AcquireOnRegistration))\n\tuserHasSecurityKey := c.Stash().Get(StashPathUserHasSecurityKey).Bool()\n\tattachmentSupported := c.Stash().Get(StashPathSecurityKeyAttachmentSupported).Bool()\n\tuserHasOTPSecret := c.Stash().Get(StashPathUserHasOTPSecret).Bool()\n\n\tif !userHasSecurityKey && !userHasOTPSecret && mfaLoginEnabled && mfaAcquireConfigured {\n\t\t// The user has no MFA methods set up but MFA is enabled and configured for acquisition.\n\n\t\tdeviceSupportsMFAMethod := mfaConfig.TOTP.Enabled || attachmentSupported\n\n\t\tif !deviceSupportsMFAMethod {\n\t\t\t// The device or browser does not support a suitable MFA method.\n\n\t\t\tif !mfaConfig.Optional {\n\t\t\t\t// Show error when onboarding is required.\n\t\t\t\tc.SetFlowError(ErrorPlatformAuthenticatorRequired)\n\t\t\t} else {\n\t\t\t\t// Skip onboarding, when optional.\n\t\t\t}\n\t\t} else {\n\t\t\tc.ScheduleStates(StateMFAMethodChooser)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/hook_verify_attestation_response.go",
    "content": "package shared\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/intern\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype VerifyAttestationResponse struct {\n\tAction\n}\n\nfunc (h VerifyAttestationResponse) Execute(c flowpilot.HookExecutionContext) error {\n\tdeps := h.GetDeps(c)\n\n\tif !c.Stash().Get(StashPathWebauthnSessionDataID).Exists() {\n\t\treturn errors.New(\"webauthn_session_data_id does not exist in the stash\")\n\t}\n\n\tsessionDataID, err := uuid.FromString(c.Stash().Get(StashPathWebauthnSessionDataID).String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse webauthn_session_data_id: %w\", err)\n\t}\n\n\tuserID, err := uuid.FromString(c.Stash().Get(StashPathUserID).String())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse user_id into a uuid: %w\", err)\n\t}\n\n\tusername := c.Stash().Get(StashPathUsername).String()\n\temail := c.Stash().Get(StashPathEmail).String()\n\n\tparams := services.VerifyAttestationResponseParams{\n\t\tTx:            deps.Tx,\n\t\tSessionDataID: sessionDataID,\n\t\tPublicKey:     c.Input().Get(\"public_key\").String(),\n\t\tUserID:        userID,\n\t\tEmail:         &email,\n\t\tUsername:      &username,\n\t}\n\n\tcredential, err := deps.WebauthnService.VerifyAttestationResponse(params)\n\tif err != nil {\n\t\tif errors.Is(err, services.ErrInvalidWebauthnCredential) {\n\t\t\tc.SetFlowError(ErrorPasskeyInvalid.Wrap(err))\n\t\t\treturn nil\n\t\t}\n\n\t\treturn fmt.Errorf(\"failed to verify attestation response: %w\", err)\n\t}\n\n\tmfaOnly := c.Stash().Get(StashPathCreateMFAOnlyCredential).Bool()\n\tcredentialModel := intern.WebauthnCredentialToModel(credential, userID, false, false, mfaOnly, deps.AuthenticatorMetadata)\n\terr = c.Stash().Set(fmt.Sprintf(\"%s.-1\", StashPathWebauthnCredentials), credentialModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set webauthn_credential to the stash: %w\", err)\n\t}\n\n\terr = c.Stash().Set(StashPathUserHasWebauthnCredential, true)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set user_has_webauthn_credential to the stash: %w\", err)\n\t}\n\n\tif mfaOnly {\n\t\terr = c.Stash().Set(StashPathUserHasSecurityKey, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set user_has_security_key to the stash: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow/shared/links.go",
    "content": "package shared\n\nimport \"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\n// Link categories enumeration.\nconst (\n\tCategoryLegal flowpilot.LinkCategory = \"legal\"\n\tCategoryOauth flowpilot.LinkCategory = \"oauth\"\n\tCategoryOther flowpilot.LinkCategory = \"other\"\n)\n\n// LegalLink creates a new link with legal the category \"legal\".\nfunc LegalLink(name string, href string) flowpilot.Link {\n\treturn flowpilot.NewLink(name, CategoryLegal, href).Target(flowpilot.LinkTargetBlank)\n}\n\n// OAuthLink creates a new link with legal the category \"oauth\".\nfunc OAuthLink(name string, href string) flowpilot.Link {\n\treturn flowpilot.NewLink(name, CategoryOauth, href)\n}\n\n// OtherLink creates a new link with legal the category \"other\".\nfunc OtherLink(name string, href string) flowpilot.Link {\n\treturn flowpilot.NewLink(name, CategoryOther, href)\n}\n"
  },
  {
    "path": "backend/flow_api/flow/user_details/action_set_email.go",
    "content": "package user_details\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype EmailAddressSet struct {\n\tshared.Action\n}\n\nfunc (a EmailAddressSet) GetName() flowpilot.ActionName {\n\treturn shared.ActionEmailAddressSet\n}\n\nfunc (a EmailAddressSet) GetDescription() string {\n\treturn \"Set a new email address.\"\n}\n\nfunc (a EmailAddressSet) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tc.AddInputs(flowpilot.StringInput(\"email\").\n\t\tRequired(!deps.Cfg.Email.Optional).\n\t\tMaxLength(deps.Cfg.Email.MaxLength).\n\t\tPreserve(true).\n\t\tTrimSpace(true).\n\t\tLowerCase(true))\n}\n\nfunc (a EmailAddressSet) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\temail := c.Input().Get(\"email\").String()\n\n\terr := c.Stash().Set(shared.StashPathEmail, email)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to stash email address: %w\", err)\n\t}\n\n\texistingEmail, err := deps.Persister.GetEmailPersisterWithConnection(deps.Tx).FindByAddress(email)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get email from db: %w\", err)\n\t}\n\n\tif deps.Cfg.Email.RequireVerification {\n\t\t// Email verification is enabled. Send an email regardless of whether the email address exists, but select the\n\t\t// appropriate passcode template beforehand.\n\t\tif existingEmail != nil {\n\t\t\terr = c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailRegistrationAttempted)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\terr = c.Stash().Set(shared.StashPathPasscodeTemplate, shared.PasscodeTemplateEmailVerification)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set passcode_template to the stash: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tif err = c.Stash().Set(shared.StashPathLoginOnboardingCreateEmail, true); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to set login_onboarding_create_email to the stash: %w\", err)\n\t\t}\n\n\t\treturn c.Continue(shared.StatePasscodeConfirmation)\n\t}\n\n\t// Email verification is turned off, hence we can display an error if the email already exists, or continue the flow\n\t// without passcode verification otherwise.\n\tif existingEmail != nil {\n\t\tc.Input().SetError(\"email\", shared.ErrorEmailAlreadyExists)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tif err = c.Stash().Set(shared.StashPathLoginOnboardingCreateEmail, true); err != nil {\n\t\treturn fmt.Errorf(\"failed to set login_onboarding_create_email to the stash: %w\", err)\n\t}\n\n\tc.PreventRevert()\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/user_details/action_set_username.go",
    "content": "package user_details\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype UsernameSet struct {\n\tshared.Action\n}\n\nfunc (a UsernameSet) GetName() flowpilot.ActionName {\n\treturn shared.ActionUsernameCreate\n}\n\nfunc (a UsernameSet) GetDescription() string {\n\treturn \"Set a new username.\"\n}\n\nfunc (a UsernameSet) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tc.AddInputs(flowpilot.StringInput(\"username\").\n\t\tRequired(!deps.Cfg.Username.Optional).\n\t\tMinLength(deps.Cfg.Username.MinLength).\n\t\tMaxLength(deps.Cfg.Username.MaxLength).\n\t\tTrimSpace(true).\n\t\tLowerCase(true))\n}\n\nfunc (a UsernameSet) Execute(c flowpilot.ExecutionContext) error {\n\tdeps := a.GetDeps(c)\n\n\tif valid := c.ValidateInputData(); !valid {\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tuserID := uuid.FromStringOrNil(c.Stash().Get(shared.StashPathUserID).String())\n\tusername := c.Input().Get(\"username\").String()\n\n\tif !services.ValidateUsername(username) {\n\t\tc.Input().SetError(\"username\", shared.ErrorInvalidUsername)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tduplicateUsername, err := deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).GetByName(username)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user from db: %w\", err)\n\t}\n\n\tif duplicateUsername != nil && duplicateUsername.ID.String() != userID.String() {\n\t\tc.Input().SetError(\"username\", shared.ErrorUsernameAlreadyExists)\n\t\treturn c.Error(flowpilot.ErrorFormDataInvalid)\n\t}\n\n\tusernameModel := models.NewUsername(userID, username)\n\terr = deps.Persister.GetUsernamePersisterWithConnection(deps.Tx).Create(*usernameModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create username: %w\", err)\n\t}\n\n\tc.PreventRevert()\n\n\tutils.NotifyUserChange(deps.HttpContext, deps.Tx, deps.Persister, events.UserUsernameCreate, userID)\n\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow/user_details/action_skip_email.go",
    "content": "package user_details\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype SkipEmail struct {\n\tshared.Action\n}\n\nfunc (a SkipEmail) GetName() flowpilot.ActionName {\n\treturn shared.ActionSkip\n}\n\nfunc (a SkipEmail) GetDescription() string {\n\treturn \"Skip\"\n}\n\nfunc (a SkipEmail) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Email.Optional {\n\t\tc.SuspendAction()\n\t}\n}\n\nfunc (a SkipEmail) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue()\n\n}\n"
  },
  {
    "path": "backend/flow_api/flow/user_details/action_skip_username.go",
    "content": "package user_details\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n)\n\ntype SkipUsername struct {\n\tshared.Action\n}\n\nfunc (a SkipUsername) GetName() flowpilot.ActionName {\n\treturn shared.ActionSkip\n}\n\nfunc (a SkipUsername) GetDescription() string {\n\treturn \"Skip\"\n}\n\nfunc (a SkipUsername) Initialize(c flowpilot.InitializationContext) {\n\tdeps := a.GetDeps(c)\n\n\tif !deps.Cfg.Username.Optional {\n\t\tc.SuspendAction()\n\t}\n}\nfunc (a SkipUsername) Execute(c flowpilot.ExecutionContext) error {\n\treturn c.Continue()\n}\n"
  },
  {
    "path": "backend/flow_api/flow_locker/flow_locker.go",
    "content": "package flow_locker\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n)\n\n// FlowLocker provides an interface for locking flow execution by flow ID\ntype FlowLocker interface {\n\t// Lock acquires a lock for the given flow ID\n\t// Returns an unlock function that must be called when done\n\tLock(ctx context.Context, flowID uuid.UUID) (unlock func(context.Context) error, err error)\n}\n\n// NewFlowLocker creates a FlowLocker based on configuration\nfunc NewFlowLocker(cfg config.FlowLocker) (FlowLocker, error) {\n\tif !cfg.Enabled {\n\t\treturn NewNoOpLocker(), nil\n\t}\n\n\tswitch cfg.Store {\n\tcase config.FLOW_LOCKER_STORE_REDIS:\n\t\treturn NewRedisLocker(RedisLockerConfig{\n\t\t\tAddress:  cfg.Redis.Address,\n\t\t\tPassword: cfg.Redis.Password,\n\t\t\tExpiry:   cfg.TTL,\n\t\t}), nil\n\tcase config.FLOW_LOCKER_STORE_IN_MEMORY:\n\t\treturn NewMemoryLocker(), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported flow locker store: %s\", cfg.Store)\n\t}\n}\n"
  },
  {
    "path": "backend/flow_api/flow_locker/flow_locker_test.go",
    "content": "package flow_locker\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n)\n\nfunc TestNewFlowLocker(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\tcfg         config.FlowLocker\n\t\twantType    string // \"noop\", \"memory\", \"redis\", or \"error\"\n\t\twantErr     bool\n\t\terrContains string\n\t}{\n\t\t{\n\t\t\tname: \"disabled returns NoOpLocker\",\n\t\t\tcfg: config.FlowLocker{\n\t\t\t\tEnabled: false,\n\t\t\t\tStore:   config.FLOW_LOCKER_STORE_IN_MEMORY,\n\t\t\t},\n\t\t\twantType: \"noop\",\n\t\t\twantErr:  false,\n\t\t},\n\t\t{\n\t\t\tname: \"in_memory store returns MemoryLocker\",\n\t\t\tcfg: config.FlowLocker{\n\t\t\t\tEnabled: true,\n\t\t\t\tStore:   config.FLOW_LOCKER_STORE_IN_MEMORY,\n\t\t\t},\n\t\t\twantType: \"memory\",\n\t\t\twantErr:  false,\n\t\t},\n\t\t{\n\t\t\tname: \"redis store with config returns RedisLocker\",\n\t\t\tcfg: config.FlowLocker{\n\t\t\t\tEnabled: true,\n\t\t\t\tStore:   config.FLOW_LOCKER_STORE_REDIS,\n\t\t\t\tRedis: &config.RedisConfig{\n\t\t\t\t\tAddress:  \"localhost:6379\",\n\t\t\t\t\tPassword: \"secret\",\n\t\t\t\t},\n\t\t\t\tTTL: 30 * time.Second,\n\t\t\t},\n\t\t\twantType: \"redis\",\n\t\t\twantErr:  false,\n\t\t},\n\t\t{\n\t\t\tname: \"unsupported store type returns error\",\n\t\t\tcfg: config.FlowLocker{\n\t\t\t\tEnabled: true,\n\t\t\t\tStore:   \"unsupported_store\",\n\t\t\t},\n\t\t\twantType:    \"error\",\n\t\t\twantErr:     true,\n\t\t\terrContains: \"unsupported flow locker store\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty store type returns error\",\n\t\t\tcfg: config.FlowLocker{\n\t\t\t\tEnabled: true,\n\t\t\t\tStore:   \"\",\n\t\t\t},\n\t\t\twantType:    \"error\",\n\t\t\twantErr:     true,\n\t\t\terrContains: \"unsupported flow locker store\",\n\t\t},\n\t\t{\n\t\t\tname: \"disabled with invalid config still returns NoOpLocker\",\n\t\t\tcfg: config.FlowLocker{\n\t\t\t\tEnabled: false,\n\t\t\t\tStore:   \"invalid\",\n\t\t\t},\n\t\t\twantType: \"noop\",\n\t\t\twantErr:  false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tlocker, err := NewFlowLocker(tt.cfg)\n\n\t\t\tif tt.wantErr {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\tassert.Contains(t, err.Error(), tt.errContains)\n\t\t\t\tassert.Nil(t, locker)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotNil(t, locker)\n\n\t\t\t// Check the type of locker returned\n\t\t\tswitch tt.wantType {\n\t\t\tcase \"noop\":\n\t\t\t\t_, ok := locker.(*NoOpLocker)\n\t\t\t\tassert.True(t, ok, \"expected NoOpLocker but got %T\", locker)\n\t\t\tcase \"memory\":\n\t\t\t\t_, ok := locker.(*MemoryLocker)\n\t\t\t\tassert.True(t, ok, \"expected MemoryLocker but got %T\", locker)\n\t\t\tcase \"redis\":\n\t\t\t\tredisLocker, ok := locker.(*RedisLocker)\n\t\t\t\tassert.True(t, ok, \"expected RedisLocker but got %T\", locker)\n\n\t\t\t\t// Verify configuration was passed correctly\n\t\t\t\tif ok {\n\t\t\t\t\tassert.Equal(t, tt.cfg.TTL, redisLocker.expiry)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/flow_api/flow_locker/memory.go",
    "content": "package flow_locker\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\n// MemoryLocker implements FlowLocker using fail-fast locking (i.e. concurrent requests do not wait)\ntype MemoryLocker struct {\n\tmu    sync.Mutex\n\tlocks map[uuid.UUID]bool\n}\n\n// NewMemoryLocker creates a new in-memory flow locker\nfunc NewMemoryLocker() *MemoryLocker {\n\treturn &MemoryLocker{\n\t\tlocks: make(map[uuid.UUID]bool),\n\t}\n}\n\n// Lock tries to acquire a lock for the given flow ID\n// Returns error immediately if lock is already held.\nfunc (m *MemoryLocker) Lock(ctx context.Context, flowID uuid.UUID) (func(context.Context) error, error) {\n\t// Check if context is already canceled before attempting lock\n\tif err := ctx.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\tif m.locks[flowID] {\n\t\treturn nil, fmt.Errorf(\"flow %s is already being processed\", flowID)\n\t}\n\n\tm.locks[flowID] = true\n\n\tunlock := func(ctx context.Context) error {\n\t\tm.mu.Lock()\n\t\tdelete(m.locks, flowID)\n\t\tm.mu.Unlock()\n\n\t\treturn nil\n\t}\n\n\treturn unlock, nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow_locker/memory_test.go",
    "content": "package flow_locker\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMemoryLocker_Lock_Success(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock, err := locker.Lock(ctx, flowID)\n\n\trequire.NoError(t, err)\n\trequire.NotNil(t, unlock)\n\n\t// Clean up\n\terr = unlock(ctx)\n\tassert.NoError(t, err)\n}\n\nfunc TestMemoryLocker_Lock_FailFast_WhenAlreadyLocked(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\t// First lock succeeds\n\tunlock1, err := locker.Lock(ctx, flowID)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, unlock1)\n\n\t// Second lock on same flow ID should fail immediately\n\tunlock2, err := locker.Lock(ctx, flowID)\n\n\tassert.Error(t, err)\n\tassert.Nil(t, unlock2)\n\tassert.Contains(t, err.Error(), \"already being processed\")\n\n\t// Clean up first lock\n\terr = unlock1(ctx)\n\tassert.NoError(t, err)\n}\n\nfunc TestMemoryLocker_Lock_SucceedsAfterUnlock(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\t// First lock\n\tunlock1, err := locker.Lock(ctx, flowID)\n\trequire.NoError(t, err)\n\terr = unlock1(ctx)\n\trequire.NoError(t, err)\n\n\t// Second lock should now succeed\n\tunlock2, err := locker.Lock(ctx, flowID)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, unlock2)\n\terr = unlock2(ctx)\n\tassert.NoError(t, err)\n}\n\nfunc TestMemoryLocker_Lock_DifferentFlowIDs(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID1 := uuid.Must(uuid.NewV4())\n\tflowID2 := uuid.Must(uuid.NewV4())\n\n\t// Lock first flow\n\tunlock1, err := locker.Lock(ctx, flowID1)\n\trequire.NoError(t, err)\n\n\t// Lock second flow should succeed (different ID)\n\tunlock2, err := locker.Lock(ctx, flowID2)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, unlock2)\n\n\t// Clean up\n\terr = unlock2(ctx)\n\tassert.NoError(t, err)\n\terr = unlock1(ctx)\n\tassert.NoError(t, err)\n}\n\nfunc TestMemoryLocker_Unlock_CanBeCalled_Multiple(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock, err := locker.Lock(ctx, flowID)\n\trequire.NoError(t, err)\n\n\t// First unlock should succeed\n\terr = unlock(ctx)\n\tassert.NoError(t, err)\n\n\t// Subsequent unlocks should not panic (even if they're no-ops)\n\tassert.NotPanics(t, func() {\n\t\tunlock(ctx)\n\t\tunlock(ctx)\n\t})\n}\n\nfunc TestMemoryLocker_ConcurrentLockAttempts(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tnumGoroutines := 10\n\tsuccessCount := 0\n\tfailCount := 0\n\tvar mu sync.Mutex\n\tvar wg sync.WaitGroup\n\n\twg.Add(numGoroutines)\n\n\t// Launch multiple goroutines trying to lock the same flow\n\tfor i := 0; i < numGoroutines; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\n\t\t\tmu.Lock()\n\t\t\tif err == nil {\n\t\t\t\tsuccessCount++\n\t\t\t\t// Hold lock briefly\n\t\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\t\tunlockErr := unlock(ctx)\n\t\t\t\tif unlockErr != nil {\n\t\t\t\t\tt.Errorf(\"unlock failed: %v\", unlockErr)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfailCount++\n\t\t\t}\n\t\t\tmu.Unlock()\n\t\t}()\n\t}\n\n\twg.Wait()\n\n\t// Exactly one should succeed, rest should fail\n\tassert.Equal(t, 1, successCount, \"exactly one lock should succeed\")\n\tassert.Equal(t, numGoroutines-1, failCount, \"all others should fail\")\n}\n\nfunc TestMemoryLocker_ConcurrentDifferentFlows(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\n\tnumFlows := 100\n\tvar wg sync.WaitGroup\n\twg.Add(numFlows)\n\n\terrors := make(chan error, numFlows*2) // Both lock and unlock errors\n\n\t// Lock different flows concurrently\n\tfor i := 0; i < numFlows; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tflowID := uuid.Must(uuid.NewV4())\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\n\t\t\tif err != nil {\n\t\t\t\terrors <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Simulate work\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\n\t\t\tif unlockErr := unlock(ctx); unlockErr != nil {\n\t\t\t\terrors <- unlockErr\n\t\t\t}\n\t\t}()\n\t}\n\n\twg.Wait()\n\tclose(errors)\n\n\t// All should succeed since they're different flow IDs\n\tfor err := range errors {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n}\n\nfunc TestMemoryLocker_MemoryLeak_LocksAreCleanedUp(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\n\t// Lock and unlock many flows\n\tfor i := 0; i < 1000; i++ {\n\t\tflowID := uuid.Must(uuid.NewV4())\n\t\tunlock, err := locker.Lock(ctx, flowID)\n\t\trequire.NoError(t, err)\n\t\terr = unlock(ctx)\n\t\trequire.NoError(t, err)\n\t}\n\n\t// Check that the map is cleaned up\n\tlocker.mu.Lock()\n\tmapSize := len(locker.locks)\n\tlocker.mu.Unlock()\n\n\tassert.Equal(t, 0, mapSize, \"locks map should be empty after all unlocks\")\n}\n\nfunc TestMemoryLocker_RaceCondition(t *testing.T) {\n\t// This test is designed to catch race conditions when run with -race flag\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tvar wg sync.WaitGroup\n\titerations := 100\n\n\t// Goroutine 1: Repeatedly lock and unlock\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor i := 0; i < iterations; i++ {\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\t\t\tif err == nil {\n\t\t\t\ttime.Sleep(time.Microsecond)\n\t\t\t\tif unlockErr := unlock(ctx); unlockErr != nil {\n\t\t\t\t\tt.Errorf(\"unlock failed: %v\", unlockErr)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttime.Sleep(time.Microsecond)\n\t\t}\n\t}()\n\n\t// Goroutine 2: Repeatedly try to lock\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor i := 0; i < iterations; i++ {\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\t\t\tif err == nil {\n\t\t\t\ttime.Sleep(time.Microsecond)\n\t\t\t\tif unlockErr := unlock(ctx); unlockErr != nil {\n\t\t\t\t\tt.Errorf(\"unlock failed: %v\", unlockErr)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttime.Sleep(time.Microsecond)\n\t\t}\n\t}()\n\n\twg.Wait()\n}\n\nfunc TestMemoryLocker_ContextCancellation_DoesNotAffectLocking(t *testing.T) {\n\tlocker := NewMemoryLocker()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\t// Create a context (not canceled yet)\n\tctx, cancel := context.WithCancel(context.Background())\n\n\t// Acquire lock with VALID context - this succeeds\n\tunlock, err := locker.Lock(ctx, flowID)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, unlock)\n\n\t// Cancel the context AFTER lock is acquired\n\tcancel()\n\n\t// Try to acquire the SAME lock from a NEW context,\n\t// verify lock is still held\n\t_, err = locker.Lock(context.Background(), flowID)\n\tassert.Error(t, err)\n\n\t// Clean up - unlock should still work with original (canceled) context\n\terr = unlock(ctx)\n\tassert.NoError(t, err)\n}\n\nfunc TestMemoryLocker_SimulateRealWorldScenario(t *testing.T) {\n\t// Simulate 5 parallel requests for the same flow (like your k6 test)\n\tlocker := NewMemoryLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tresults := make(chan bool, 5)\n\tvar wg sync.WaitGroup\n\twg.Add(5)\n\n\t// Simulate 5 parallel requests\n\tfor i := 0; i < 5; i++ {\n\t\tgo func(requestNum int) {\n\t\t\tdefer wg.Done()\n\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\t\t\tif err != nil {\n\t\t\t\t// Request failed to acquire lock\n\t\t\t\tresults <- false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Simulate processing (e.g., verifying passcode)\n\t\t\ttime.Sleep(50 * time.Millisecond)\n\n\t\t\tunlockErr := unlock(ctx)\n\t\t\tif unlockErr != nil {\n\t\t\t\tt.Errorf(\"unlock failed for request %d: %v\", requestNum, unlockErr)\n\t\t\t\tresults <- false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tresults <- true\n\t\t}(i)\n\t}\n\n\twg.Wait()\n\tclose(results)\n\n\t// Count successes\n\tsuccessCount := 0\n\tfor success := range results {\n\t\tif success {\n\t\t\tsuccessCount++\n\t\t}\n\t}\n\n\t// Only 1 request should have succeeded\n\tassert.Equal(t, 1, successCount, \"only one request should acquire the lock\")\n}\n"
  },
  {
    "path": "backend/flow_api/flow_locker/noop.go",
    "content": "package flow_locker\n\nimport (\n\t\"context\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\n// NoOpLocker is a no-op implementation that doesn't actually lock\ntype NoOpLocker struct{}\n\n// NewNoOpLocker creates a new no-op locker\nfunc NewNoOpLocker() *NoOpLocker {\n\treturn &NoOpLocker{}\n}\n\n// Lock does nothing and returns a no-op unlock function\nfunc (n *NoOpLocker) Lock(ctx context.Context, flowID uuid.UUID) (func(context.Context) error, error) {\n\treturn func(context.Context) error { return nil }, nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow_locker/redis.go",
    "content": "package flow_locker\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/go-redsync/redsync/v4\"\n\t\"github.com/go-redsync/redsync/v4/redis/redigo\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/gomodule/redigo/redis\"\n)\n\n// RedisLocker implements FlowLocker using Redis with Redlock\ntype RedisLocker struct {\n\trs     *redsync.Redsync\n\texpiry time.Duration\n}\n\n// RedisLockerConfig holds configuration for RedisLocker\ntype RedisLockerConfig struct {\n\tAddress  string\n\tPassword string\n\tExpiry   time.Duration\n}\n\n// NewRedisLocker creates a new Redis-based flow locker\nfunc NewRedisLocker(config RedisLockerConfig) *RedisLocker {\n\tif config.Expiry == 0 {\n\t\tconfig.Expiry = 15 * time.Second\n\t}\n\n\tpool := &redis.Pool{\n\t\tDial: func() (redis.Conn, error) {\n\t\t\tc, err := redis.Dial(\"tcp\", config.Address, redis.DialPassword(config.Password))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\treturn c, nil\n\t\t},\n\t}\n\n\trs := redsync.New(redigo.NewPool(pool))\n\n\treturn &RedisLocker{\n\t\trs:     rs,\n\t\texpiry: config.Expiry,\n\t}\n}\n\n// Lock acquires a distributed lock for the given flow ID\nfunc (r *RedisLocker) Lock(ctx context.Context, flowID uuid.UUID) (func(context.Context) error, error) {\n\t// Check if context is already canceled before attempting lock\n\tif err := ctx.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"context error: %w\", err)\n\t}\n\n\tmutex := r.rs.NewMutex(\n\t\t\"flow:lock:\"+flowID.String(),\n\t\tredsync.WithExpiry(r.expiry),\n\t\tredsync.WithTries(1),\n\t)\n\n\tif err := mutex.LockContext(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tunlock := func(ctx context.Context) error {\n\t\tif ok, err := mutex.UnlockContext(ctx); !ok || err != nil {\n\t\t\treturn fmt.Errorf(\"failed to release lock: %w\", err)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn unlock, nil\n}\n"
  },
  {
    "path": "backend/flow_api/flow_locker/redis_test.go",
    "content": "package flow_locker\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/ory/dockertest/v3\"\n\t\"github.com/ory/dockertest/v3/docker\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/stretchr/testify/suite\"\n)\n\ntype RedisLockerTestSuite struct {\n\tsuite.Suite\n\tpool         *dockertest.Pool\n\tresource     *dockertest.Resource\n\tredisAddress string\n}\n\nfunc (suite *RedisLockerTestSuite) SetupSuite() {\n\tvar err error\n\n\tsuite.pool, err = dockertest.NewPool(\"\")\n\trequire.NoError(suite.T(), err, \"Could not construct pool\")\n\n\terr = suite.pool.Client.Ping()\n\trequire.NoError(suite.T(), err, \"Could not connect to Docker\")\n\n\tsuite.resource, err = suite.pool.RunWithOptions(&dockertest.RunOptions{\n\t\tRepository: \"redis\",\n\t\tTag:        \"8-alpine\",\n\t\tEnv:        []string{},\n\t}, func(config *docker.HostConfig) {\n\t\tconfig.AutoRemove = true\n\t\tconfig.RestartPolicy = docker.RestartPolicy{Name: \"no\"}\n\t})\n\trequire.NoError(suite.T(), err, \"Could not start Redis container\")\n\n\t_ = suite.resource.Expire(600)\n\n\t// Wait for Redis to be ready\n\tsuite.redisAddress = fmt.Sprintf(\"localhost:%s\", suite.resource.GetPort(\"6379/tcp\"))\n\n\tsuite.pool.MaxWait = 30 * time.Second\n\terr = suite.pool.Retry(func() error {\n\t\tlocker := NewRedisLocker(RedisLockerConfig{\n\t\t\tAddress: suite.redisAddress,\n\t\t\tExpiry:  10 * time.Second,\n\t\t})\n\n\t\tctx := context.Background()\n\t\ttestID := uuid.Must(uuid.NewV4())\n\t\tunlock, err := locker.Lock(ctx, testID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn unlock(ctx)\n\t})\n\trequire.NoError(suite.T(), err, \"Could not connect to Redis\")\n}\n\nfunc (suite *RedisLockerTestSuite) TearDownSuite() {\n\tif suite.resource != nil {\n\t\terr := suite.pool.Purge(suite.resource)\n\t\trequire.NoError(suite.T(), err, \"Could not purge Redis container\")\n\t}\n}\n\nfunc (suite *RedisLockerTestSuite) getTestRedisLocker() *RedisLocker {\n\treturn NewRedisLocker(RedisLockerConfig{\n\t\tAddress: suite.redisAddress,\n\t\tExpiry:  10 * time.Second,\n\t})\n}\n\n// TestRedisLockerSuite runs the test suite\nfunc TestRedisLockerSuite(t *testing.T) {\n\tsuite.Run(t, new(RedisLockerTestSuite))\n}\n\nfunc (suite *RedisLockerTestSuite) TestLock_Success() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock, err := locker.Lock(ctx, flowID)\n\n\trequire.NoError(suite.T(), err)\n\trequire.NotNil(suite.T(), unlock)\n\n\terr = unlock(ctx)\n\tassert.NoError(suite.T(), err, \"unlock should succeed\")\n}\n\nfunc (suite *RedisLockerTestSuite) TestLock_FailFast_WhenAlreadyLocked() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock1, err := locker.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\trequire.NotNil(suite.T(), unlock1)\n\tdefer func() {\n\t\terr := unlock1(ctx)\n\t\tassert.NoError(suite.T(), err)\n\t}()\n\n\tunlock2, err := locker.Lock(ctx, flowID)\n\n\tassert.Error(suite.T(), err)\n\tassert.Nil(suite.T(), unlock2)\n}\n\nfunc (suite *RedisLockerTestSuite) TestLock_SucceedsAfterUnlock() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock1, err := locker.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\terr = unlock1(ctx)\n\trequire.NoError(suite.T(), err)\n\n\ttime.Sleep(10 * time.Millisecond)\n\n\tunlock2, err := locker.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\trequire.NotNil(suite.T(), unlock2)\n\terr = unlock2(ctx)\n\tassert.NoError(suite.T(), err)\n}\n\nfunc (suite *RedisLockerTestSuite) TestLock_DifferentFlowIDs() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID1 := uuid.Must(uuid.NewV4())\n\tflowID2 := uuid.Must(uuid.NewV4())\n\n\tunlock1, err := locker.Lock(ctx, flowID1)\n\trequire.NoError(suite.T(), err)\n\tdefer func() {\n\t\terr := unlock1(ctx)\n\t\tassert.NoError(suite.T(), err)\n\t}()\n\n\tunlock2, err := locker.Lock(ctx, flowID2)\n\trequire.NoError(suite.T(), err)\n\trequire.NotNil(suite.T(), unlock2)\n\tdefer func() {\n\t\terr := unlock2(ctx)\n\t\tassert.NoError(suite.T(), err)\n\t}()\n}\n\nfunc (suite *RedisLockerTestSuite) TestUnlock_ReturnsError_OnMultipleCalls() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock, err := locker.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\n\terr = unlock(ctx)\n\tassert.NoError(suite.T(), err, \"first unlock should succeed\")\n\n\terr = unlock(ctx)\n\tassert.Error(suite.T(), err, \"second unlock should fail\")\n\tassert.Contains(suite.T(), err.Error(), \"failed to release lock\")\n\n\terr = unlock(ctx)\n\tassert.Error(suite.T(), err, \"third unlock should also fail\")\n}\n\nfunc (suite *RedisLockerTestSuite) TestConcurrentLockAttempts() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tnumGoroutines := 10\n\tsuccessCount := 0\n\tfailCount := 0\n\tvar mu sync.Mutex\n\tvar wg sync.WaitGroup\n\n\twg.Add(numGoroutines)\n\n\tfor i := 0; i < numGoroutines; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\n\t\t\tmu.Lock()\n\t\t\tif err == nil {\n\t\t\t\tsuccessCount++\n\t\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\t\tunlockErr := unlock(ctx)\n\t\t\t\tif unlockErr != nil {\n\t\t\t\t\tsuite.T().Errorf(\"unlock failed: %v\", unlockErr)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfailCount++\n\t\t\t}\n\t\t\tmu.Unlock()\n\t\t}()\n\t}\n\n\twg.Wait()\n\n\tassert.Equal(suite.T(), 1, successCount, \"exactly one lock should succeed\")\n\tassert.Equal(suite.T(), numGoroutines-1, failCount, \"all others should fail\")\n}\n\nfunc (suite *RedisLockerTestSuite) TestConcurrentDifferentFlows() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\n\tnumFlows := 100\n\tvar wg sync.WaitGroup\n\twg.Add(numFlows)\n\n\terrors := make(chan error, numFlows*2) // Both lock and unlock errors\n\n\tfor i := 0; i < numFlows; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tflowID := uuid.Must(uuid.NewV4())\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\n\t\t\tif err != nil {\n\t\t\t\terrors <- fmt.Errorf(\"lock error: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\n\t\t\tif unlockErr := unlock(ctx); unlockErr != nil {\n\t\t\t\terrors <- fmt.Errorf(\"unlock error: %w\", unlockErr)\n\t\t\t}\n\t\t}()\n\t}\n\n\twg.Wait()\n\tclose(errors)\n\n\tfor err := range errors {\n\t\tsuite.T().Errorf(\"unexpected error: %v\", err)\n\t}\n}\n\nfunc (suite *RedisLockerTestSuite) TestLockExpiry() {\n\tlocker := NewRedisLocker(RedisLockerConfig{\n\t\tAddress: suite.redisAddress,\n\t\tExpiry:  1 * time.Second,\n\t})\n\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\t_, err := locker.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\n\t// Unlock ignored, we don't unlock but let it expire\n\ttime.Sleep(2 * time.Second)\n\n\t// Should be able to acquire lock again after expiry\n\tunlock2, err := locker.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\trequire.NotNil(suite.T(), unlock2)\n\n\terr = unlock2(ctx)\n\tassert.NoError(suite.T(), err)\n}\n\nfunc (suite *RedisLockerTestSuite) TestContextCancellation() {\n\tlocker := suite.getTestRedisLocker()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tcancel()\n\n\tunlock, err := locker.Lock(ctx, flowID)\n\tassert.Error(suite.T(), err)\n\tassert.Nil(suite.T(), unlock)\n}\n\nfunc (suite *RedisLockerTestSuite) TestContextTimeout() {\n\tlocker := suite.getTestRedisLocker()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock1, err := locker.Lock(context.Background(), flowID)\n\trequire.NoError(suite.T(), err)\n\tdefer func() {\n\t\terr := unlock1(context.Background())\n\t\tassert.NoError(suite.T(), err)\n\t}()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\tdefer cancel()\n\n\tunlock2, err := locker.Lock(ctx, flowID)\n\tassert.Error(suite.T(), err)\n\tassert.Nil(suite.T(), unlock2)\n}\n\nfunc (suite *RedisLockerTestSuite) TestSimulateRealWorldScenario() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tresults := make(chan bool, 5)\n\tvar wg sync.WaitGroup\n\twg.Add(5)\n\n\tfor i := 0; i < 5; i++ {\n\t\tgo func(requestNum int) {\n\t\t\tdefer wg.Done()\n\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\t\t\tif err != nil {\n\t\t\t\tresults <- false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttime.Sleep(50 * time.Millisecond)\n\n\t\t\tunlockErr := unlock(ctx)\n\t\t\tif unlockErr != nil {\n\t\t\t\tsuite.T().Errorf(\"unlock failed for request %d: %v\", requestNum, unlockErr)\n\t\t\t\tresults <- false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tresults <- true\n\t\t}(i)\n\t}\n\n\twg.Wait()\n\tclose(results)\n\n\tsuccessCount := 0\n\tfor success := range results {\n\t\tif success {\n\t\t\tsuccessCount++\n\t\t}\n\t}\n\n\tassert.Equal(suite.T(), 1, successCount, \"only one request should acquire the lock\")\n}\n\nfunc (suite *RedisLockerTestSuite) TestMultipleInstances() {\n\tlocker1 := suite.getTestRedisLocker()\n\tlocker2 := NewRedisLocker(RedisLockerConfig{\n\t\tAddress: suite.redisAddress,\n\t\tExpiry:  10 * time.Second,\n\t})\n\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock1, err := locker1.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\tdefer func() {\n\t\terr := unlock1(ctx)\n\t\tassert.NoError(suite.T(), err)\n\t}()\n\n\tunlock2, err := locker2.Lock(ctx, flowID)\n\tassert.Error(suite.T(), err)\n\tassert.Nil(suite.T(), unlock2)\n}\n\nfunc (suite *RedisLockerTestSuite) TestRaceCondition() {\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tvar wg sync.WaitGroup\n\titerations := 50\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor i := 0; i < iterations; i++ {\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\t\t\tif err == nil {\n\t\t\t\ttime.Sleep(5 * time.Millisecond)\n\t\t\t\tif unlockErr := unlock(ctx); unlockErr != nil {\n\t\t\t\t\tsuite.T().Errorf(\"unlock failed: %v\", unlockErr)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\t\t}\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor i := 0; i < iterations; i++ {\n\t\t\tunlock, err := locker.Lock(ctx, flowID)\n\t\t\tif err == nil {\n\t\t\t\ttime.Sleep(5 * time.Millisecond)\n\t\t\t\tif unlockErr := unlock(ctx); unlockErr != nil {\n\t\t\t\t\tsuite.T().Errorf(\"unlock failed: %v\", unlockErr)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\t\t}\n\t}()\n\n\twg.Wait()\n}\n\nfunc (suite *RedisLockerTestSuite) TestRedisLocker_UnlockReturnsError() {\n\t// Test that calling unlock multiple times returns an error\n\t// This validates that unlock properly returns errors without needing to simulate Redis failures\n\tlocker := suite.getTestRedisLocker()\n\tctx := context.Background()\n\tflowID := uuid.Must(uuid.NewV4())\n\n\tunlock, err := locker.Lock(ctx, flowID)\n\trequire.NoError(suite.T(), err)\n\trequire.NotNil(suite.T(), unlock)\n\n\terr = unlock(ctx)\n\tassert.NoError(suite.T(), err, \"first unlock should succeed\")\n\n\tunlockCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n\tdefer cancel()\n\n\terr = unlock(unlockCtx)\n\tassert.Error(suite.T(), err, \"second unlock should return an error\")\n\tassert.Contains(suite.T(), err.Error(), \"failed to release lock\")\n}\n"
  },
  {
    "path": "backend/flow_api/handler.go",
    "content": "package flow_api\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\techojwt \"github.com/labstack/echo-jwt/v4\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/rs/zerolog\"\n\tzeroLogger \"github.com/rs/zerolog/log\"\n\t\"github.com/sethvargo/go-limiter\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow_locker\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\ntype FlowPilotHandler struct {\n\tPersister                   persistence.Persister\n\tCfg                         config.Config\n\tSecurityNotificationService services.SecurityNotification\n\tPasscodeService             services.Passcode\n\tPasswordService             services.Password\n\tWebauthnService             services.WebauthnService\n\tSamlService                 saml.Service\n\tSessionManager              session.Manager\n\tOTPRateLimiter              limiter.Store\n\tPasscodeRateLimiter         limiter.Store\n\tPasswordRateLimiter         limiter.Store\n\tTokenExchangeRateLimiter    limiter.Store\n\tAuthenticatorMetadata       mapper.AuthenticatorMetadata\n\tAuditLogger                 auditlog.Logger\n\tFlowLocker                  flow_locker.FlowLocker\n}\n\nfunc (h *FlowPilotHandler) RegistrationFlowHandler(c echo.Context) error {\n\tregistrationFlow := flow.NewRegistrationFlow(h.Cfg.Debug)\n\treturn h.executeFlow(c, registrationFlow)\n}\n\nfunc (h *FlowPilotHandler) LoginFlowHandler(c echo.Context) error {\n\tloginFlow := flow.NewLoginFlow(h.Cfg.Debug)\n\te := h.executeFlow(c, loginFlow)\n\treturn e\n}\n\nfunc (h *FlowPilotHandler) ProfileFlowHandler(c echo.Context) error {\n\tprofileFlow := flow.NewProfileFlow(h.Cfg.Debug)\n\n\tif err := h.validateSession(c); err != nil {\n\t\tflowResult := profileFlow.ResultFromError(err)\n\t\treturn c.JSON(flowResult.GetStatus(), flowResult.GetResponse())\n\t}\n\n\treturn h.executeFlow(c, profileFlow)\n}\n\nfunc (h *FlowPilotHandler) TokenExchangeFlowHandler(c echo.Context) error {\n\tsamlIdPInitiatedLoginFlow := flow.NewTokenExchangeFlow(h.Cfg.Debug)\n\treturn h.executeFlow(c, samlIdPInitiatedLoginFlow)\n}\n\nfunc (h *FlowPilotHandler) validateSession(c echo.Context) error {\n\tlookup := fmt.Sprintf(\"header:Authorization:Bearer,cookie:%s\", h.Cfg.Session.Cookie.GetName())\n\textractors, err := echojwt.CreateExtractors(lookup)\n\n\tif err != nil {\n\t\treturn flowpilot.ErrorTechnical.Wrap(err)\n\t}\n\n\tvar lastExtractorErr, lastTokenErr error\n\tfor _, extractor := range extractors {\n\t\tauths, extractorErr := extractor(c)\n\t\tif extractorErr != nil {\n\t\t\tlastExtractorErr = extractorErr\n\t\t\tcontinue\n\t\t}\n\t\tfor _, auth := range auths {\n\t\t\ttoken, tokenErr := h.SessionManager.Verify(auth)\n\t\t\tif tokenErr != nil {\n\t\t\t\tlastTokenErr = tokenErr\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// check that the session id is stored in the database\n\t\t\tsessionId, ok := token.Get(\"session_id\")\n\t\t\tif !ok {\n\t\t\t\tlastTokenErr = errors.New(\"no session id found in token\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsessionID, err := uuid.FromString(sessionId.(string))\n\t\t\tif err != nil {\n\t\t\t\tlastTokenErr = errors.New(\"session id has wrong format\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tsessionModel, err := h.Persister.GetSessionPersister().Get(sessionID)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to get session from database: %w\", err)\n\t\t\t}\n\t\t\tif sessionModel == nil {\n\t\t\t\tlastTokenErr = fmt.Errorf(\"session id not found in database\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Update lastUsed field\n\t\t\tsessionModel.LastUsed = time.Now().UTC()\n\t\t\terr = h.Persister.GetSessionPersister().Update(*sessionModel)\n\t\t\tif err != nil {\n\t\t\t\treturn dto.ToHttpError(err)\n\t\t\t}\n\n\t\t\tc.Set(\"session\", token)\n\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tif lastTokenErr != nil {\n\t\treturn shared.ErrorUnauthorized.Wrap(lastTokenErr)\n\t} else if lastExtractorErr != nil {\n\t\treturn shared.ErrorUnauthorized.Wrap(lastExtractorErr)\n\t}\n\n\treturn nil\n}\n\nfunc (h *FlowPilotHandler) executeFlow(c echo.Context, flow flowpilot.Flow) error {\n\tconst queryParamKey = \"action\"\n\n\tvar err error\n\tvar inputData flowpilot.InputData\n\tvar flowResult flowpilot.FlowResult\n\tvar unlock func(context.Context) error\n\tvar flowID uuid.UUID\n\n\tif c.QueryParam(queryParamKey) != \"\" {\n\t\terr = c.Bind(&inputData)\n\t\tif err != nil {\n\t\t\tflowResult = flow.ResultFromError(flowpilot.ErrorTechnical.Wrap(err))\n\t\t\th.logFlowResult(c, flowResult)\n\t\t\treturn c.JSON(flowResult.GetStatus(), flowResult.GetResponse())\n\t\t}\n\n\t\tflowID, err = extractFlowID(c.QueryParam(queryParamKey))\n\t\tif err != nil {\n\t\t\tflowResult = flow.ResultFromError(flowpilot.ErrorTechnical.Wrap(err))\n\t\t\th.logFlowResult(c, flowResult)\n\t\t\treturn c.JSON(flowResult.GetStatus(), flowResult.GetResponse())\n\t\t}\n\n\t\tunlock, err = h.FlowLocker.Lock(c.Request().Context(), flowID)\n\t\tif err != nil {\n\t\t\tflowResult = flow.ResultFromError(flowpilot.ErrorTechnical.Wrap(fmt.Errorf(\"could not acquire lock: %w\", err)))\n\t\t\th.logFlowResult(c, flowResult)\n\t\t\treturn c.JSON(flowResult.GetStatus(), flowResult.GetResponse())\n\t\t}\n\t}\n\n\ttxFunc := func(tx *pop.Connection) error {\n\t\tdeps := &shared.Dependencies{\n\t\t\tCfg:                         h.Cfg,\n\t\t\tOTPRateLimiter:              h.OTPRateLimiter,\n\t\t\tPasscodeRateLimiter:         h.PasscodeRateLimiter,\n\t\t\tPasswordRateLimiter:         h.PasswordRateLimiter,\n\t\t\tTokenExchangeRateLimiter:    h.TokenExchangeRateLimiter,\n\t\t\tTx:                          tx,\n\t\t\tPersister:                   h.Persister,\n\t\t\tHttpContext:                 c,\n\t\t\tSessionManager:              h.SessionManager,\n\t\t\tSecurityNotificationService: h.SecurityNotificationService,\n\t\t\tPasscodeService:             h.PasscodeService,\n\t\t\tPasswordService:             h.PasswordService,\n\t\t\tWebauthnService:             h.WebauthnService,\n\t\t\tSamlService:                 h.SamlService,\n\t\t\tAuthenticatorMetadata:       h.AuthenticatorMetadata,\n\t\t\tAuditLogger:                 h.AuditLogger,\n\t\t}\n\n\t\tflow.Set(\"deps\", deps)\n\n\t\tflowResult, err = flow.Execute(persistence.NewFlowPersister(tx),\n\t\t\tflowpilot.WithQueryParamKey(queryParamKey),\n\t\t\tflowpilot.WithQueryParamValue(c.QueryParam(queryParamKey)),\n\t\t\tflowpilot.WithInputData(inputData),\n\t\t\tflowpilot.UseCompression(!h.Cfg.Debug))\n\n\t\treturn err\n\t}\n\n\terr = h.Persister.Transaction(txFunc)\n\tif err != nil {\n\t\tflowResult = flow.ResultFromError(err)\n\t}\n\n\tif unlock != nil {\n\t\tunlockCtx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second)\n\t\tdefer cancel()\n\n\t\tif unlockErr := unlock(unlockCtx); unlockErr != nil {\n\t\t\tuErr := fmt.Errorf(\"failed to release lock: %w\", unlockErr)\n\t\t\tif err != nil {\n\t\t\t\tflowResult = flow.ResultFromError(errors.Join(err, uErr))\n\t\t\t} else {\n\t\t\t\tflowResult = flow.ResultFromError(flowpilot.ErrorTechnical.Wrap(uErr))\n\t\t\t}\n\t\t}\n\t}\n\n\th.logFlowResult(c, flowResult)\n\n\treturn c.JSON(flowResult.GetStatus(), flowResult.GetResponse())\n}\n\nfunc (h *FlowPilotHandler) logFlowResult(c echo.Context, flowResult flowpilot.FlowResult) {\n\tlog := zeroLogger.Info().\n\t\tStr(\"time_unix\", strconv.FormatInt(time.Now().Unix(), 10)).\n\t\tStr(\"id\", c.Response().Header().Get(echo.HeaderXRequestID)).\n\t\tStr(\"remote_ip\", c.RealIP()).Str(\"host\", c.Request().Host).\n\t\tStr(\"method\", c.Request().Method).Str(\"uri\", c.Request().RequestURI).\n\t\tStr(\"user_agent\", c.Request().UserAgent()).Int(\"status\", flowResult.GetStatus()).\n\t\tStr(\"referer\", c.Request().Referer())\n\tif flowResult.GetResponse().Error != nil {\n\t\tlog.Str(\"error\", fmt.Sprintf(\"%s\", flowResult.GetResponse().Error.Code))\n\t\tif flowResult.GetResponse().Error.Internal != nil {\n\t\t\tlog.Str(\"error_internal\", *flowResult.GetResponse().Error.Internal)\n\t\t}\n\t}\n\tlog.Send()\n}\n\nfunc init() {\n\tzerolog.TimeFieldFormat = time.RFC3339Nano\n}\n\n// extractFlowID extracts just the flow ID from \"action@flowID\" format\nfunc extractFlowID(queryParamValue string) (uuid.UUID, error) {\n\tparts := strings.Split(queryParamValue, \"@\")\n\tif len(parts) != 2 {\n\t\treturn uuid.Nil, fmt.Errorf(\"invalid flow id format\")\n\t}\n\treturn uuid.FromString(parts[1])\n}\n"
  },
  {
    "path": "backend/flow_api/services/device_trust.go",
    "content": "package services\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\n// DeviceTrustEntry represents a single user's device trust token entry\ntype DeviceTrustEntry struct {\n\tUserID      uuid.UUID\n\tDeviceToken string\n}\n\nconst (\n\t// entrySeparator separates multiple user entries in the cookie\n\tentrySeparator = \"|\"\n\t// fieldSeparator separates user ID from token within an entry\n\tfieldSeparator = \":\"\n)\n\ntype DeviceTrustService struct {\n\tPersister   persistence.TrustedDevicePersister\n\tCfg         config.Config\n\tHttpContext echo.Context\n}\n\nfunc (s DeviceTrustService) CreateTrustedDevice(userID uuid.UUID, deviceToken string) error {\n\tdeviceID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate device id: %w\", err)\n\t}\n\n\ttrustedDeviceModel := models.TrustedDevice{\n\t\tID:          deviceID,\n\t\tUserID:      userID,\n\t\tDeviceToken: deviceToken,\n\t\tExpiresAt:   time.Now().Add(s.Cfg.MFA.DeviceTrustDuration).UTC(),\n\t\tCreatedAt:   time.Now().UTC(),\n\t\tUpdatedAt:   time.Now().UTC(),\n\t}\n\n\terr = s.Persister.Create(trustedDeviceModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store trusted device: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (s DeviceTrustService) CheckDeviceTrust(userID uuid.UUID) bool {\n\tif userID.IsNil() || s.Cfg.MFA.DeviceTrustPolicy == \"never\" {\n\t\treturn false\n\t}\n\n\tcookieName := s.Cfg.MFA.DeviceTrustCookieName\n\tcookie, _ := s.HttpContext.Cookie(cookieName)\n\n\tif cookie == nil {\n\t\treturn false\n\t}\n\n\tentries := s.ParseDeviceTrustCookie(cookie.Value)\n\n\t// Handle legacy format (single token without user ID)\n\tif entries == nil && cookie.Value != \"\" {\n\t\t// Legacy: look up token in DB to check if it belongs to this user\n\t\ttrustedDevice, err := s.Persister.FindByDeviceToken(cookie.Value)\n\t\tif err == nil && trustedDevice != nil &&\n\t\t\ttime.Now().UTC().Before(trustedDevice.ExpiresAt.UTC()) &&\n\t\t\ttrustedDevice.UserID.String() == userID.String() {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\t// New format: find entry for this user\n\tfor _, entry := range entries {\n\t\tif entry.UserID.String() == userID.String() {\n\t\t\ttrustedDevice, err := s.Persister.FindByDeviceToken(entry.DeviceToken)\n\t\t\tif err == nil && trustedDevice != nil &&\n\t\t\t\ttime.Now().UTC().Before(trustedDevice.ExpiresAt.UTC()) &&\n\t\t\t\ttrustedDevice.UserID.String() == userID.String() {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (s DeviceTrustService) GenerateRandomToken(length int) (string, error) {\n\tbytes := make([]byte, length)\n\t_, err := rand.Read(bytes)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.URLEncoding.EncodeToString(bytes), nil\n}\n\n// ParseDeviceTrustCookie parses a composite device trust cookie value into individual entries.\n// Returns nil if the cookie is empty or in legacy format (single token without user ID).\n// Legacy format detection: no separators means it's a single token.\nfunc (s DeviceTrustService) ParseDeviceTrustCookie(cookieValue string) []DeviceTrustEntry {\n\tif cookieValue == \"\" {\n\t\treturn nil\n\t}\n\n\t// Legacy format detection (no separators = single token)\n\tif !strings.Contains(cookieValue, entrySeparator) && !strings.Contains(cookieValue, fieldSeparator) {\n\t\treturn nil // Caller handles legacy migration\n\t}\n\n\tvar entries []DeviceTrustEntry\n\tparts := strings.Split(cookieValue, entrySeparator)\n\n\tfor _, part := range parts {\n\t\tfields := strings.SplitN(part, fieldSeparator, 2)\n\t\tif len(fields) != 2 {\n\t\t\tcontinue // Skip malformed entries\n\t\t}\n\n\t\tuserID, err := uuid.FromString(fields[0])\n\t\tif err != nil {\n\t\t\tcontinue // Skip invalid user IDs\n\t\t}\n\n\t\tentries = append(entries, DeviceTrustEntry{\n\t\t\tUserID:      userID,\n\t\t\tDeviceToken: fields[1],\n\t\t})\n\t}\n\n\treturn entries\n}\n\n// SerializeDeviceTrustCookie serializes device trust entries into a composite cookie value.\n// Format: <user_id_1>:<token_1>|<user_id_2>:<token_2>|...\nfunc (s DeviceTrustService) SerializeDeviceTrustCookie(entries []DeviceTrustEntry) string {\n\tif len(entries) == 0 {\n\t\treturn \"\"\n\t}\n\n\tparts := make([]string, len(entries))\n\tfor i, entry := range entries {\n\t\tparts[i] = entry.UserID.String() + fieldSeparator + entry.DeviceToken\n\t}\n\n\treturn strings.Join(parts, entrySeparator)\n}\n"
  },
  {
    "path": "backend/flow_api/services/email.go",
    "content": "package services\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/mail\"\n\t\"gopkg.in/gomail.v2\"\n)\n\ntype Email struct {\n\trenderer *mail.Renderer\n\tmailer   mail.Mailer\n\tcfg      config.Config\n}\n\nfunc NewEmailService(cfg config.Config) (*Email, error) {\n\trenderer, err := mail.NewRenderer()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmailer, err := mail.NewMailer(cfg.EmailDelivery.SMTP)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to create mailer: %w\", err))\n\t}\n\n\treturn &Email{\n\t\trenderer,\n\t\tmailer,\n\t\tcfg,\n\t}, nil\n}\n\n// SendEmail sends an email to the emailAddress with the given subject and body.\nfunc (s *Email) SendEmail(emailAddress, subject, body, htmlBody string) error {\n\tmessage := gomail.NewMessage()\n\tmessage.SetAddressHeader(\"To\", emailAddress, \"\")\n\tmessage.SetAddressHeader(\"From\", s.cfg.EmailDelivery.FromAddress, s.cfg.EmailDelivery.FromName)\n\tmessage.SetHeader(\"Subject\", subject)\n\tmessage.SetBody(\"text/plain\", body)\n\tmessage.AddAlternative(\"text/html\", htmlBody)\n\n\tif err := s.mailer.Send(message); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// RenderSubject renders a subject with the given template. Must be \"subject_[template_name]\".\nfunc (s *Email) RenderSubject(lang, template string, data map[string]interface{}) string {\n\treturn s.renderer.Translate(lang, fmt.Sprintf(\"subject_%s\", template), data)\n}\n\n// RenderBodyPlain renders the body with the given template. The template name must be the name of the template without the\n// content type and the file ending. E.g. when the file is created as \"email_verification_text.tmpl\" then the template\n// name is just \"email_verification\"\nfunc (s *Email) RenderBodyPlain(lang, template string, data map[string]interface{}) (string, error) {\n\treturn s.renderer.RenderPlain(template, lang, data)\n}\n\nfunc (s *Email) RenderBodyHTML(lang, template string, data map[string]interface{}) (string, error) {\n\treturn s.renderer.RenderHTML(template, lang, data)\n}\n"
  },
  {
    "path": "backend/flow_api/services/passcode.go",
    "content": "package services\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nvar maxPasscodeTries = 3\n\nvar (\n\tErrorPasscodeInvalid            = errors.New(\"passcode invalid\")\n\tErrorPasscodeNotFound           = errors.New(\"passcode not found\")\n\tErrorPasscodeExpired            = errors.New(\"passcode is expired\")\n\tErrorPasscodeMaxAttemptsReached = errors.New(\"the passcode was entered wrong too many times\")\n)\n\ntype SendPasscodeParams struct {\n\tTemplate     string\n\tEmailAddress string\n\tLanguage     string\n}\n\ntype ValidatePasscodeParams struct {\n\tTx         *pop.Connection\n\tPasscodeID uuid.UUID\n}\n\ntype SendPasscodeResult struct {\n\tPasscodeModel models.Passcode\n\tSubject       string\n\tBodyPlain     string\n\tBodyHTML      string\n\tCode          string\n}\n\ntype Passcode interface {\n\tValidatePasscode(ValidatePasscodeParams) (bool, error)\n\tSendPasscode(*pop.Connection, SendPasscodeParams) (*SendPasscodeResult, error)\n\tVerifyPasscodeCode(tx *pop.Connection, passcodeID uuid.UUID, passcode string) error\n}\n\ntype passcode struct {\n\temailService      Email\n\tpasscodeGenerator crypto.PasscodeGenerator\n\tpersister         persistence.Persister\n\tcfg               config.Config\n}\n\nfunc NewPasscodeService(cfg config.Config, emailService Email, persister persistence.Persister) Passcode {\n\tpasscodeGenerator := crypto.NewNumericPasscodeGenerator()\n\tswitch cfg.Email.PasscodeCharset {\n\tcase config.PasscodeCharsetAlphanumeric:\n\t\tpasscodeGenerator = crypto.NewAlphanumericPasscodeGenerator()\n\t}\n\treturn &passcode{\n\t\temailService,\n\t\tpasscodeGenerator,\n\t\tpersister,\n\t\tcfg,\n\t}\n}\n\nfunc (s *passcode) ValidatePasscode(p ValidatePasscodeParams) (bool, error) {\n\tif !p.PasscodeID.IsNil() {\n\t\t_, err := s.getPasscode(p.Tx, p.PasscodeID)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, ErrorPasscodeNotFound) || errors.Is(err, ErrorPasscodeExpired) || errors.Is(err, ErrorPasscodeMaxAttemptsReached) {\n\t\t\t\treturn false, nil\n\t\t\t} else {\n\t\t\t\treturn false, fmt.Errorf(\"failed to get passcode from db: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\treturn true, nil\n\t}\n\n\treturn false, nil\n}\n\nfunc (s *passcode) VerifyPasscodeCode(tx *pop.Connection, passcodeID uuid.UUID, value string) error {\n\tpasscodePersister := s.persister.GetPasscodePersisterWithConnection(tx)\n\tpasscodeModel, err := s.getPasscode(tx, passcodeID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = bcrypt.CompareHashAndPassword([]byte(passcodeModel.Code), []byte(value))\n\tif err != nil {\n\t\tpasscodeModel.TryCount += 1\n\n\t\terr = passcodePersister.Update(*passcodeModel)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to update passcode: %w\", err)\n\t\t}\n\n\t\tif passcodeModel.TryCount >= maxPasscodeTries {\n\t\t\treturn ErrorPasscodeMaxAttemptsReached\n\t\t}\n\n\t\treturn ErrorPasscodeInvalid\n\t}\n\n\terr = passcodePersister.Delete(*passcodeModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete passcode from db: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *passcode) SendPasscode(tx *pop.Connection, p SendPasscodeParams) (*SendPasscodeResult, error) {\n\tcode, err := s.passcodeGenerator.Generate()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thashedPasscode, err := bcrypt.GenerateFromPassword([]byte(code), 12)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpasscodeId, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnow := time.Now().UTC()\n\tpasscodeModel := models.Passcode{\n\t\tID:        passcodeId,\n\t\tTtl:       s.cfg.Email.PasscodeTtl,\n\t\tCode:      string(hashedPasscode),\n\t\tTryCount:  0,\n\t\tCreatedAt: now,\n\t\tUpdatedAt: now,\n\t}\n\n\terr = s.persister.GetPasscodePersisterWithConnection(tx).Create(passcodeModel)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdurationTTL := time.Duration(passcodeModel.Ttl) * time.Second\n\n\tsubjectData := map[string]interface{}{\n\t\t\"Code\": code,\n\t\t\"TTL\":  fmt.Sprintf(\"%.0f\", durationTTL.Minutes()),\n\t}\n\n\tsubject := s.emailService.RenderSubject(p.Language, p.Template, subjectData)\n\n\tbodyData := map[string]interface{}{\n\t\t\"Code\":        code,\n\t\t\"TTL\":         fmt.Sprintf(\"%.0f\", durationTTL.Minutes()),\n\t\t\"ServiceName\": s.cfg.Service.Name,\n\t\t\"Subject\":     subject,\n\t}\n\n\tbodyPlain, err := s.emailService.RenderBodyPlain(p.Language, p.Template, bodyData)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbodyHTML, err := s.emailService.RenderBodyHTML(p.Language, p.Template, bodyData)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif s.cfg.EmailDelivery.Enabled {\n\t\terr = s.emailService.SendEmail(p.EmailAddress, subject, bodyPlain, bodyHTML)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn &SendPasscodeResult{\n\t\tPasscodeModel: passcodeModel,\n\t\tSubject:       subject,\n\t\tBodyPlain:     bodyPlain,\n\t\tBodyHTML:      bodyHTML,\n\t\tCode:          code,\n\t}, nil\n}\n\nfunc (s *passcode) getPasscode(tx *pop.Connection, passcodeID uuid.UUID) (*models.Passcode, error) {\n\tpasscodePersister := s.persister.GetPasscodePersisterWithConnection(tx)\n\n\tpasscodeModel, err := passcodePersister.Get(passcodeID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get passcode from db: %w\", err)\n\t}\n\n\tif passcodeModel == nil {\n\t\treturn nil, ErrorPasscodeNotFound\n\t}\n\n\texpirationTime := passcodeModel.CreatedAt.Add(time.Duration(passcodeModel.Ttl) * time.Second)\n\tif expirationTime.Before(time.Now().UTC()) {\n\t\treturn nil, ErrorPasscodeExpired\n\t}\n\n\tif passcodeModel.TryCount >= maxPasscodeTries {\n\t\treturn nil, ErrorPasscodeMaxAttemptsReached\n\t}\n\n\treturn passcodeModel, nil\n}\n"
  },
  {
    "path": "backend/flow_api/services/password.go",
    "content": "package services\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"time\"\n)\n\nvar (\n\tErrorPasswordInvalid = errors.New(\"password invalid\")\n)\n\ntype Password interface {\n\tVerifyPassword(tx *pop.Connection, userId uuid.UUID, password string) error\n\tRecoverPassword(tx *pop.Connection, userId uuid.UUID, newPassword string) error\n\tCreatePassword(tx *pop.Connection, userId uuid.UUID, newPassword string) error\n\tUpdatePassword(tx *pop.Connection, passwordCredentialModel *models.PasswordCredential, newPassword string) error\n}\n\ntype password struct {\n\tpersister persistence.Persister\n}\n\nfunc NewPasswordService(persister persistence.Persister) Password {\n\treturn &password{\n\t\tpersister,\n\t}\n}\n\nfunc (s password) VerifyPassword(tx *pop.Connection, userId uuid.UUID, password string) error {\n\tuser, err := s.persister.GetUserPersisterWithConnection(tx).Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tif user == nil {\n\t\treturn ErrorPasswordInvalid\n\t}\n\n\tpw, err := s.persister.GetPasswordCredentialPersisterWithConnection(tx).GetByUserID(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error retrieving password credential: %w\", err)\n\t}\n\n\tif pw == nil {\n\t\treturn ErrorPasswordInvalid\n\t}\n\n\tif err = bcrypt.CompareHashAndPassword([]byte(pw.Password), []byte(password)); err != nil {\n\t\treturn ErrorPasswordInvalid\n\t}\n\n\treturn nil\n}\n\nfunc (s password) RecoverPassword(tx *pop.Connection, userId uuid.UUID, newPassword string) error {\n\tpasswordPersister := s.persister.GetPasswordCredentialPersisterWithConnection(tx)\n\n\tpasswordCredentialModel, err := passwordPersister.GetByUserID(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get password credential by user id: %w\", err)\n\t}\n\n\tif passwordCredentialModel == nil {\n\t\terr = s.CreatePassword(tx, userId, newPassword)\n\t} else {\n\t\terr = s.UpdatePassword(tx, passwordCredentialModel, newPassword)\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s password) CreatePassword(tx *pop.Connection, userId uuid.UUID, newPassword string) error {\n\thashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), 12)\n\tif err != nil {\n\t\treturn ErrorPasswordInvalid\n\t}\n\n\tpasswordCredentialModel := models.NewPasswordCredential(userId, string(hashedPassword))\n\n\terr = s.persister.GetPasswordCredentialPersisterWithConnection(tx).Create(*passwordCredentialModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set password: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (s password) UpdatePassword(tx *pop.Connection, passwordCredentialModel *models.PasswordCredential, newPassword string) error {\n\thashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), 12)\n\tif err != nil {\n\t\treturn ErrorPasswordInvalid\n\t}\n\n\tpasswordCredentialModel.Password = string(hashedPassword)\n\tpasswordCredentialModel.UpdatedAt = time.Now().UTC()\n\n\terr = s.persister.GetPasswordCredentialPersisterWithConnection(tx).Update(*passwordCredentialModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update password: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/services/security_notification.go",
    "content": "package services\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\twebhookUtils \"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n)\n\ntype SendSecurityNotificationParams struct {\n\tTemplate     string\n\tUserID       uuid.UUID\n\tEmailAddress string\n\tBodyData     map[string]interface{}            // Data used in templates\n\tData         *webhook.SecurityNotificationData // Data used for (serialized) webhook 'data' payload\n\tHttpContext  echo.Context\n\tUserContext  models.User\n}\n\ntype SecurityNotification interface {\n\tSendNotification(*pop.Connection, SendSecurityNotificationParams) error\n}\n\ntype securityNotification struct {\n\tcfg          config.Config\n\temailService Email\n\tauditLog     auditlog.Logger\n\tpersister    persistence.Persister\n}\n\nfunc NewSecurityNotificationService(cfg config.Config, emailService Email, persister persistence.Persister, auditLog auditlog.Logger) SecurityNotification {\n\treturn &securityNotification{\n\t\tcfg:          cfg,\n\t\temailService: emailService,\n\t\tauditLog:     auditLog,\n\t\tpersister:    persister,\n\t}\n}\n\nfunc (s securityNotification) SendNotification(tx *pop.Connection, p SendSecurityNotificationParams) error {\n\tlanguage := p.HttpContext.Request().Header.Get(\"X-Language\")\n\n\tsubject := s.emailService.RenderSubject(language, p.Template, map[string]interface{}{\n\t\t\"ServiceName\": s.cfg.Service.Name,\n\t})\n\n\tif p.BodyData == nil {\n\t\tp.BodyData = map[string]interface{}{}\n\t}\n\n\tp.BodyData[\"ServiceName\"] = s.cfg.Service.Name\n\n\tbodyPlain, err := s.emailService.RenderBodyPlain(language, p.Template, p.BodyData)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbodyHTML, err := s.emailService.RenderBodyHTML(language, p.Template, p.BodyData)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdeliveredByHanko := false\n\tif s.cfg.EmailDelivery.Enabled {\n\t\terr = s.emailService.SendEmail(p.EmailAddress, subject, bodyPlain, bodyHTML)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdeliveredByHanko = true\n\t}\n\n\tif p.Data == nil {\n\t\tp.Data = &webhook.SecurityNotificationData{}\n\t}\n\n\tp.Data.Template = p.Template\n\tp.Data.ServiceName = s.cfg.Service.Name\n\n\twebhookData := webhook.EmailSend{\n\t\tSubject:          subject,\n\t\tBodyPlain:        bodyPlain,\n\t\tBody:             bodyHTML,\n\t\tToEmailAddress:   p.EmailAddress,\n\t\tDeliveredByHanko: deliveredByHanko,\n\t\tLanguage:         language,\n\t\tType:             \"security_notification\",\n\t\tData:             p.Data,\n\t}\n\n\terr = webhookUtils.TriggerWebhooks(p.HttpContext, tx, events.EmailSend, webhookData)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Prefer full user context if available; fall back to a bare user by ID.\n\tuserForAudit := &models.User{ID: p.UserID}\n\tif p.UserContext.ID != uuid.Nil {\n\t\tuserForAudit = &p.UserContext\n\t}\n\n\tauditLogDetails := []auditlog.DetailOption{\n\t\tauditlog.Detail(\"template\", p.Template),\n\t\tauditlog.Detail(\"email_address\", p.EmailAddress),\n\t\tauditlog.Detail(\"delivered_by_hanko\", fmt.Sprintf(\"%t\", deliveredByHanko)),\n\t}\n\n\terr = s.auditLog.CreateWithConnection(\n\t\ttx,\n\t\tp.HttpContext,\n\t\tmodels.AuditLogSecurityNotificationSent,\n\t\tuserForAudit,\n\t\tnil,\n\t\tauditLogDetails...,\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/flow_api/services/user.go",
    "content": "package services\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"regexp\"\n)\n\nfunc UserCanDoThirdParty(cfg config.Config, identities models.Identities) bool {\n\tfor _, identity := range identities {\n\t\tif provider := cfg.ThirdParty.Providers.Get(identity.ProviderID); provider != nil {\n\t\t\treturn provider.Enabled\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc UserCanDoSaml(cfg config.Config, identities models.Identities) bool {\n\tfor _, identity := range identities {\n\t\tif provider := cfg.Saml.GetProviderByDomain(identity.ProviderID); provider != nil {\n\t\t\treturn cfg.Saml.Enabled && provider.Enabled\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc ValidateUsername(name string) bool {\n\tre := regexp.MustCompile(`^\\w+$`)\n\treturn re.MatchString(name)\n}\n"
  },
  {
    "path": "backend/flow_api/services/webauthn.go",
    "content": "package services\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype GenerateRequestOptionsPasskeyParams struct {\n\tTx   *pop.Connection\n\tUser *models.User\n}\n\ntype GenerateRequestOptionsSecurityKeyParams struct {\n\tTx     *pop.Connection\n\tUserID uuid.UUID\n}\n\ntype VerifyAssertionResponseParams struct {\n\tTx                *pop.Connection\n\tSessionDataID     uuid.UUID\n\tAssertionResponse string\n\tIsMFA             bool\n}\n\ntype GenerateCreationOptionsParams struct {\n\tTx       *pop.Connection\n\tUserID   uuid.UUID\n\tEmail    *string\n\tUsername *string\n}\n\ntype VerifyAttestationResponseParams struct {\n\tTx            *pop.Connection\n\tSessionDataID uuid.UUID\n\tPublicKey     string\n\tUserID        uuid.UUID\n\tEmail         *string\n\tUsername      *string\n}\n\ntype WebauthnService interface {\n\tGenerateRequestOptionsPasskey(GenerateRequestOptionsPasskeyParams) (*models.WebauthnSessionData, *protocol.CredentialAssertion, error)\n\tGenerateRequestOptionsSecurityKey(GenerateRequestOptionsSecurityKeyParams) (*models.WebauthnSessionData, *protocol.CredentialAssertion, error)\n\tVerifyAssertionResponse(VerifyAssertionResponseParams) (*models.User, error)\n\tGenerateCreationOptionsPasskey(GenerateCreationOptionsParams) (*models.WebauthnSessionData, *protocol.CredentialCreation, error)\n\tGenerateCreationOptionsSecurityKey(GenerateCreationOptionsParams) (*models.WebauthnSessionData, *protocol.CredentialCreation, error)\n\tVerifyAttestationResponse(VerifyAttestationResponseParams) (*webauthn.Credential, error)\n}\n\ntype webauthnUser struct {\n\tid       uuid.UUID\n\temail    *string\n\tusername *string\n}\n\nfunc (user webauthnUser) WebAuthnID() []byte {\n\treturn user.id.Bytes()\n}\n\nfunc (user webauthnUser) WebAuthnName() string {\n\tif user.email != nil && len(*user.email) > 0 {\n\t\treturn *user.email\n\t}\n\n\tif user.username != nil {\n\t\treturn *user.username\n\t}\n\n\treturn \"\"\n}\n\nfunc (user webauthnUser) WebAuthnDisplayName() string {\n\tif user.username != nil && len(*user.username) > 0 {\n\t\treturn *user.username\n\t}\n\n\tif user.email != nil {\n\t\treturn *user.email\n\t}\n\n\treturn \"\"\n}\n\nfunc (user webauthnUser) WebAuthnCredentials() []webauthn.Credential {\n\treturn nil\n}\n\nfunc (user webauthnUser) WebAuthnIcon() string {\n\treturn \"\"\n}\n\nvar (\n\tErrInvalidWebauthnCredential        = errors.New(\"this passkey cannot be used anymore\")\n\tErrInvalidWebauthnCredentialMFAOnly = errors.New(\"this credential can be used as a second factor security key only\")\n)\n\ntype webauthnService struct {\n\tcfg       config.Config\n\tpersister persistence.Persister\n}\n\nfunc NewWebauthnService(cfg config.Config, persister persistence.Persister) WebauthnService {\n\treturn &webauthnService{cfg: cfg, persister: persister}\n}\n\nfunc (s *webauthnService) generateRequestOptions(tx *pop.Connection, user webauthn.User, opts ...webauthn.LoginOption) (*models.WebauthnSessionData, *protocol.CredentialAssertion, error) {\n\tvar options *protocol.CredentialAssertion\n\tvar sessionData *webauthn.SessionData\n\tvar err error\n\tif !reflect.ValueOf(user).IsNil() {\n\t\toptions, sessionData, err = s.cfg.Webauthn.Handler.BeginLogin(user, opts...)\n\t} else {\n\t\toptions, sessionData, err = s.cfg.Webauthn.Handler.BeginDiscoverableLogin(opts...)\n\t}\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to create webauthn assertion options for discoverable login: %w\", err)\n\t}\n\n\twebAuthnSessionDataModel, err := models.NewWebauthnSessionDataFrom(sessionData, models.WebauthnOperationAuthentication)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to generate a new webauthn session data model: %w\", err)\n\t}\n\n\terr = s.persister.GetWebauthnSessionDataPersisterWithConnection(tx).Create(*webAuthnSessionDataModel)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to store webauthn assertion session data: %w\", err)\n\t}\n\n\treturn webAuthnSessionDataModel, options, nil\n}\n\nfunc (s *webauthnService) GenerateRequestOptionsPasskey(p GenerateRequestOptionsPasskeyParams) (*models.WebauthnSessionData, *protocol.CredentialAssertion, error) {\n\tuserVerificationRequirement := protocol.UserVerificationRequirement(s.cfg.Passkey.UserVerification)\n\n\treturn s.generateRequestOptions(p.Tx,\n\t\tp.User,\n\t\twebauthn.WithUserVerification(userVerificationRequirement),\n\t)\n}\n\nfunc (s *webauthnService) GenerateRequestOptionsSecurityKey(p GenerateRequestOptionsSecurityKeyParams) (*models.WebauthnSessionData, *protocol.CredentialAssertion, error) {\n\tuserVerificationRequirement := protocol.UserVerificationRequirement(s.cfg.MFA.SecurityKeys.UserVerification)\n\n\tuserModel, err := s.persister.GetUserPersisterWithConnection(p.Tx).Get(p.UserID)\n\tif err != nil || userModel == nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to get user from db: %w\", err)\n\t}\n\n\tcredentialModels, err := s.persister.GetWebauthnCredentialPersisterWithConnection(p.Tx).GetFromUser(p.UserID)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to get webauthn credentials from db: %w\", err)\n\t}\n\n\tdescriptors, err := credentialModels.GetWebauthnDescriptors()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn s.generateRequestOptions(p.Tx,\n\t\tuserModel,\n\t\twebauthn.WithUserVerification(userVerificationRequirement),\n\t\twebauthn.WithAllowedCredentials(descriptors),\n\t)\n}\n\nfunc (s *webauthnService) VerifyAssertionResponse(p VerifyAssertionResponseParams) (*models.User, error) {\n\tcredentialAssertionData, err := protocol.ParseCredentialRequestResponseBody(strings.NewReader(p.AssertionResponse))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", err, ErrInvalidWebauthnCredential)\n\t}\n\n\tsessionDataModel, err := s.persister.GetWebauthnSessionDataPersisterWithConnection(p.Tx).Get(p.SessionDataID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get session data from db: %w\", err)\n\t}\n\n\tcredentialModel, err := s.persister.GetWebauthnCredentialPersisterWithConnection(p.Tx).Get(credentialAssertionData.ID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get webauthncredential from db: %w\", err)\n\t}\n\n\tif credentialModel == nil {\n\t\treturn nil, ErrInvalidWebauthnCredential\n\t}\n\n\tif !p.IsMFA && credentialModel.MFAOnly {\n\t\treturn nil, ErrInvalidWebauthnCredentialMFAOnly\n\t}\n\n\twebAuthnUser, userModel, err := s.GetWebAuthnUser(p.Tx, *credentialModel)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdiscoverableUserHandler := func(rawID, userHandle []byte) (webauthn.User, error) {\n\t\treturn webAuthnUser, nil\n\t}\n\n\tsessionData := sessionDataModel.ToSessionData()\n\tif p.IsMFA || len(sessionData.AllowedCredentialIDs) > 0 {\n\t\t_, err = s.cfg.Webauthn.Handler.ValidateLogin(webAuthnUser, *sessionData, credentialAssertionData)\n\t} else {\n\t\t_, err = s.cfg.Webauthn.Handler.ValidateDiscoverableLogin(\n\t\t\tdiscoverableUserHandler,\n\t\t\t*sessionData,\n\t\t\tcredentialAssertionData,\n\t\t)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", err, ErrInvalidWebauthnCredential)\n\t}\n\n\tnow := time.Now().UTC()\n\tflags := credentialAssertionData.Response.AuthenticatorData.Flags\n\n\tcredentialModel.LastUsedAt = &now\n\tcredentialModel.BackupState = flags.HasBackupState()\n\tcredentialModel.BackupEligible = flags.HasBackupEligible()\n\n\tsignCount := int(credentialAssertionData.Response.AuthenticatorData.Counter)\n\n\tif credentialModel.SignCount > 0 && signCount > 0 && signCount <= credentialModel.SignCount {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"%w: signature counter mismatch: expected received signature count (%d) to be greater than current count (%d)\",\n\t\t\tErrInvalidWebauthnCredential,\n\t\t\tsignCount, credentialModel.SignCount,\n\t\t)\n\t}\n\n\tcredentialModel.SignCount = signCount\n\n\terr = s.persister.GetWebauthnCredentialPersisterWithConnection(p.Tx).Update(*credentialModel)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to update webauthn credential: %w\", err)\n\t}\n\n\terr = s.persister.GetWebauthnSessionDataPersisterWithConnection(p.Tx).Delete(*sessionDataModel)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to delete assertion session data: %w\", err)\n\t}\n\n\treturn userModel, nil\n}\n\nfunc (s *webauthnService) generateCreationOptions(p GenerateCreationOptionsParams, opts ...webauthn.RegistrationOption) (*models.WebauthnSessionData, *protocol.CredentialCreation, error) {\n\tuser := webauthnUser{id: p.UserID, email: p.Email, username: p.Username}\n\n\tuserModel, err := s.persister.GetUserPersisterWithConnection(p.Tx).Get(user.id)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to get user from db: %w\", err)\n\t}\n\n\t// Assemble exclude list only if user already exists (i.e. the current flow is not a registration flow).\n\tif userModel != nil {\n\t\tcredentialDescriptors, err := userModel.WebauthnCredentials.GetWebauthnDescriptors()\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to get credential descriptors from webauthn credentials: %w\", err)\n\t\t}\n\n\t\topts = append(opts, webauthn.WithExclusions(credentialDescriptors))\n\t}\n\n\toptions, sessionData, err := s.cfg.Webauthn.Handler.BeginRegistration(user, opts...)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"%s: %w\", err, ErrInvalidWebauthnCredential)\n\t}\n\n\tsessionDataModel, err := models.NewWebauthnSessionDataFrom(sessionData, models.WebauthnOperationRegistration)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to create new session data model instance: %w\", err)\n\t}\n\n\terr = s.persister.GetWebauthnSessionDataPersisterWithConnection(p.Tx).Create(*sessionDataModel)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to store session data to the db: %w\", err)\n\t}\n\n\treturn sessionDataModel, options, nil\n}\n\nfunc (s *webauthnService) GenerateCreationOptionsSecurityKey(p GenerateCreationOptionsParams) (*models.WebauthnSessionData, *protocol.CredentialCreation, error) {\n\trequireResidentKey := false\n\tauthenticatorSelection := protocol.AuthenticatorSelection{\n\t\tRequireResidentKey: &requireResidentKey,\n\t\tResidentKey:        protocol.ResidentKeyRequirementDiscouraged,\n\t\tUserVerification:   protocol.UserVerificationRequirement(s.cfg.MFA.SecurityKeys.UserVerification),\n\t}\n\n\tauthenticatorAttachment := s.cfg.MFA.SecurityKeys.AuthenticatorAttachment\n\tif authenticatorAttachment == \"platform\" || authenticatorAttachment == \"cross-platform\" {\n\t\tauthenticatorSelection.AuthenticatorAttachment = protocol.AuthenticatorAttachment(authenticatorAttachment)\n\t}\n\n\tattestationPreference := protocol.ConveyancePreference(s.cfg.Passkey.AttestationPreference)\n\n\treturn s.generateCreationOptions(p,\n\t\twebauthn.WithAuthenticatorSelection(authenticatorSelection),\n\t\twebauthn.WithConveyancePreference(attestationPreference),\n\t)\n}\n\nfunc (s *webauthnService) GenerateCreationOptionsPasskey(p GenerateCreationOptionsParams) (*models.WebauthnSessionData, *protocol.CredentialCreation, error) {\n\trequireResidentKey := true\n\tauthenticatorSelection := protocol.AuthenticatorSelection{\n\t\tRequireResidentKey: &requireResidentKey,\n\t\tResidentKey:        protocol.ResidentKeyRequirementRequired,\n\t\tUserVerification:   protocol.UserVerificationRequirement(s.cfg.Passkey.UserVerification),\n\t}\n\n\tattestationPreference := protocol.ConveyancePreference(s.cfg.Passkey.AttestationPreference)\n\n\treturn s.generateCreationOptions(p,\n\t\twebauthn.WithAuthenticatorSelection(authenticatorSelection),\n\t\twebauthn.WithConveyancePreference(attestationPreference),\n\t)\n}\n\nfunc (s *webauthnService) VerifyAttestationResponse(p VerifyAttestationResponseParams) (*webauthn.Credential, error) {\n\tcredentialCreationData, err := protocol.ParseCredentialCreationResponseBody(strings.NewReader(p.PublicKey))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse credential creation response; %w\", err)\n\t}\n\n\tsessionDataModel, err := s.persister.GetWebauthnSessionDataPersisterWithConnection(p.Tx).Get(p.SessionDataID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get session data from db: %w\", err)\n\t}\n\n\tuser := webauthnUser{id: p.UserID, email: p.Email, username: p.Username}\n\n\tsessionData := sessionDataModel.ToSessionData()\n\n\tcredential, err := s.cfg.Webauthn.Handler.CreateCredential(\n\t\tuser,\n\t\t*sessionData,\n\t\tcredentialCreationData,\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", err, ErrInvalidWebauthnCredential)\n\t}\n\n\terr = s.persister.GetWebauthnSessionDataPersisterWithConnection(p.Tx).Delete(*sessionDataModel)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to delete webauthn session data: %w\", err)\n\t}\n\n\treturn credential, nil\n}\n\nfunc (s *webauthnService) GetWebAuthnUser(tx *pop.Connection, credential models.WebauthnCredential) (webauthn.User, *models.User, error) {\n\tuser, err := s.persister.GetUserPersisterWithConnection(tx).Get(credential.UserId)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to fetch user from db: %w\", err)\n\t}\n\tif user == nil {\n\t\treturn nil, nil, ErrInvalidWebauthnCredential\n\t}\n\n\tif credential.UserHandle != nil {\n\t\treturn &webauthnUserWithCustomUserHandle{\n\t\t\tCustomUserHandle: []byte(credential.UserHandle.Handle),\n\t\t\tUser:             *user,\n\t\t}, user, nil\n\t}\n\n\treturn user, user, err\n}\n\ntype webauthnUserWithCustomUserHandle struct {\n\tmodels.User\n\tCustomUserHandle []byte\n}\n\nfunc (u *webauthnUserWithCustomUserHandle) WebAuthnID() []byte {\n\treturn u.CustomUserHandle\n}\n"
  },
  {
    "path": "backend/flow_api/static/generic_client.html",
    "content": "<html>\n<head>\n    <style>\n        body {\n            font-family: sans-serif;\n            text-indent: 3em;\n        }\n\n        button, div, li {\n            margin-left: 4em;\n        }\n\n        h1, h2, h3, h4, h5, ul {\n            margin: .5em;\n        }\n\n        h1, h2 {\n            text-indent: 0;\n        }\n\n        h3 {\n            text-indent: 1em;\n        }\n\n        h4 {\n            text-indent: 1.5em;\n        }\n\n        ul {\n            list-style-type: none;\n            padding: 0;\n        }\n\n        .link-list {\n            list-style-type: unset;\n        }\n\n        .link-list-item {\n            text-indent: 0;\n        }\n    </style>\n</head>\n<body>\n<h1>✨ Generic Flowpilot Client</h1>\n<button id=\"newLoginFlowBtn\">✍ New flow</button>\n\n<label for=\"options\"></label>\n<select id=\"options\">\n    <option value=\"login\">Login</option>\n    <option value=\"registration\">Registration</option>\n    <option value=\"profile\">Profile</option>\n</select>\n\n<div id=\"container\"></div>\n<script type=\"module\">\n    import {\n        parseCreationOptionsFromJSON,\n        parseRequestOptionsFromJSON,\n        create,\n        get\n    } from 'https://cdn.jsdelivr.net/npm/@github/webauthn-json@2.1.1/browser-ponyfill/+esm'\n\n    window.addEventListener(\"load\", () => {\n      const urlParams = new URLSearchParams(window.location.search);\n      const hankoToken = urlParams.get('hanko_token');\n      if (hankoToken) {\n        const state = JSON.parse(localStorage.getItem(\"thirdparty_oauth_state\"))\n        generateUI(state)\n        const form = document.getElementById('exchange_token');\n        form.getElementsByTagName(\"input\")[0].value = hankoToken;\n        window.history.replaceState(null, null, window.location.pathname);\n      }\n    })\n\n    let abortController = new AbortController()\n    let webauthnAssertionForConditionalUI = undefined\n\n    async function webauthnCreate(payload) {\n        const options = parseCreationOptionsFromJSON(payload)\n        const response = await create(options)\n        return JSON.stringify(response)\n    }\n\n    async function webauthnGet(payload) {\n      const options = parseRequestOptionsFromJSON(payload)\n      const response = await get(options)\n      return JSON.stringify(response)\n    }\n\n    async function webauthnGetConditional(payload) {\n      const options = parseRequestOptionsFromJSON(payload)\n      options.mediation = \"conditional\";\n\n      if (abortController) {\n        abortController.abort()\n      }\n\n      abortController = new AbortController();\n      options.signal = abortController.signal\n\n      let assertion;\n      try {\n        assertion = await get(options);\n      } catch (e) {\n        if (e.name === 'AbortError') {\n          console.warn('Conditional UI: aborting credential get request');\n          return;\n        }\n          throw new Error(e);\n      }\n\n      return JSON.stringify(assertion)\n    }\n\n    const awaitAssertionResponseForConditionalUi = async (payload) => {\n      const assertion_response = await webauthnGetConditional(payload.request_options)\n      if (assertion_response) {\n        webauthnAssertionForConditionalUI = assertion_response\n        document.getElementsByName(\"assertion_response\")[0].value = assertion_response\n      }\n    }\n\n    const resetAssertionResponseInput = () => {\n      if (document.getElementsByName(\"assertion_response\").length > 0) {\n        document.getElementsByName(\"assertion_response\")[0].value = \"\"\n      }\n    }\n\n    const passkeyRegisterHandler = async (payload) => {\n        document.getElementsByName(\"public_key\")[0].value = await webauthnCreate(payload.creation_options)\n    }\n\n    const passkeyLoginHandler = async (payload) => {\n      document.getElementsByName(\"assertion_response\")[0].value = await webauthnGet(payload.request_options)\n    }\n\n    const thirdPartyHandler = (payload) => {\n      window.location.href = payload.redirect_url;\n    }\n\n    const stateHandler = {\n        \"login_passkey\": passkeyLoginHandler,\n        \"passkey_login\": passkeyLoginHandler,\n        \"passkey_registration\": passkeyRegisterHandler,\n        \"onboarding_verify_passkey_attestation\": passkeyRegisterHandler,\n        \"webauthn_credential_verification\": passkeyRegisterHandler,\n        \"register_passkey\": passkeyRegisterHandler,\n        \"thirdparty\": thirdPartyHandler,\n    }\n\n    function generateUI(data) {\n        if (data.name === \"login_init\" && data.payload && data.payload.request_options) {\n          awaitAssertionResponseForConditionalUi(data.payload)\n        }\n\n        const container = document.getElementById('container');\n        container.innerHTML = '';\n\n        const stateHeadline = document.createElement('h2');\n        stateHeadline.innerHTML = \"📌 State: \" + data.name;\n        container.appendChild(stateHeadline);\n\n        const errorHeadline = document.createElement('h3');\n        errorHeadline.innerHTML = \"⛔ Error\";\n        container.appendChild(errorHeadline);\n\n        if (data.error) {\n            const errorEl = document.createElement('p');\n            errorEl.textContent = `Code: ${data.error.code}, Message: ${data.error.message}, Cause: ${data.error.cause}`\n            container.appendChild(errorEl);\n        } else {\n            const notAvailable = document.createElement('p');\n            notAvailable.textContent = \"[n/a]\";\n            container.appendChild(notAvailable);\n        }\n\n        const payloadHeadline = document.createElement('h3');\n        payloadHeadline.innerHTML = \"📦 Payload\";\n        container.appendChild(payloadHeadline);\n\n        if (data.payload) {\n            const pre = document.createElement('pre');\n            pre.textContent = \"\\n\" + JSON.stringify(data.payload, null, 2)\n            container.appendChild(pre);\n\n            if (data.name === \"mfa_otp_secret_creation\") {\n              const img = document.createElement(\"img\")\n              img.src = data.payload.otp_image_source\n              container.appendChild(img)\n            }\n        } else {\n            const notAvailable = document.createElement('p');\n            notAvailable.textContent = \"[n/a]\";\n            container.appendChild(notAvailable);\n        }\n\n        const executeHandlerButton = document.createElement('button');\n        executeHandlerButton.type = 'button';\n        executeHandlerButton.textContent = \"☝ Execute handler\";\n\n        if (!stateHandler[data.name]) {\n            executeHandlerButton.disabled = true\n        } else {\n            executeHandlerButton.addEventListener('click', (event) => {\n                event.preventDefault();\n                stateHandler[data.name](data.payload);\n            });\n        }\n\n        container.appendChild(executeHandlerButton);\n\n        const actionsHeadline = document.createElement('h3');\n        actionsHeadline.textContent = \"🕹 Actions\";\n        container.appendChild(actionsHeadline);\n\n        if (data.actions) {\n            Object.values(data.actions).forEach(action => {\n                const form = document.createElement('form');\n                form.action = action.href;\n                form.method = 'POST';\n                form.id = action.action;\n\n                const actionHeadline = document.createElement('h4');\n                actionHeadline.textContent = \"⚡ Action: \" + action.action;\n                form.appendChild(actionHeadline);\n\n\n                const descriptionHeadline = document.createElement('h5');\n                descriptionHeadline.textContent = \"📝 Description\";\n                form.appendChild(descriptionHeadline);\n\n                const description = document.createElement('div');\n                description.textContent = action.description;\n                form.appendChild(description);\n\n                const schemaHeadline = document.createElement('h5');\n                schemaHeadline.textContent = \"⛳ Schema\";\n                form.appendChild(schemaHeadline);\n\n                if (action.inputs) {\n                    const inputList = document.createElement('ul');\n                    Object.values(action.inputs).forEach(input => {\n                        const inputListItem = document.createElement('li');\n\n                        const label = document.createElement(\"label\");\n\n                        const flags = [];\n\n                        if (input.required) {\n                            flags.push(\"required\")\n                        } else {\n                            flags.push(\"optional\")\n                        }\n\n                        if (input.hidden) {\n                            flags.push(\"hidden\");\n                        }\n\n                        const details = (flags.length ? \" (\" + flags.join(\", \") + \")\" : \"\");\n                        label.textContent = input.name + details + \": \";\n                        inputListItem.appendChild(label)\n\n                        const inputElement = document.createElement('input');\n\n                        if (input.value) {\n                            inputElement.defaultValue = input.value\n                        }\n\n                        inputElement.name = input.name;\n                        inputElement.type = input.type;\n                        inputElement.required = input.required;\n\n                        inputListItem.appendChild(inputElement);\n\n                        if (input.error) {\n                            const error = document.createElement('p')\n                            error.textContent = \"⛔ Code: \" + input.error.code + \", Message: \" + input.error.message\n                            inputListItem.appendChild(error);\n                        }\n\n                        if (data.name === \"login_init\" && action.action === \"webauthn_verify_assertion_response\") {\n                          if (webauthnAssertionForConditionalUI) {\n                            inputElement.value = webauthnAssertionForConditionalUI\n                          }\n                          inputElement.autocomplete = \"username webauthn\"\n                        }\n\n                        inputList.appendChild(inputListItem)\n                    });\n\n                    form.appendChild(inputList);\n                } else {\n                    const notAvailable = document.createElement('p');\n                    notAvailable.textContent = \"[n/a]\";\n                    form.appendChild(notAvailable);\n                }\n\n                const submitButton = document.createElement('button');\n                submitButton.type = 'submit';\n                submitButton.textContent = \"📡 Submit\";\n                submitButton.addEventListener('click', (event) => {\n                  if (data.name === \"login_init\") {\n                    abortController.abort()\n                  }\n                  updateFlow(event, form, data.csrf_token)\n                })\n\n                form.appendChild(submitButton);\n\n                container.appendChild(form);\n            });\n        } else {\n            const notAvailable = document.createElement('p');\n            notAvailable.textContent = \"[n/a]\";\n            container.appendChild(notAvailable);\n        }\n\n        const linksHeadline = document.createElement('h3');\n        linksHeadline.textContent = \"🔗 Links\";\n        container.appendChild(linksHeadline);\n        if (data.links) {\n            const linksList = document.createElement('ul');\n            linksList.classList.add(\"link-list\")\n\n            data.links.forEach(link => {\n                const listItem = document.createElement('li')\n                listItem.classList.add(\"link-list-item\")\n                const anchor = document.createElement('a')\n                anchor.text = '[' + link.category + '] ' + link.name\n                anchor.href = link.href\n                anchor.target = link.target\n\n                listItem.appendChild(anchor)\n                linksList.appendChild(listItem)\n            })\n\n            container.appendChild(linksList)\n        }\n    }\n\n    function serializeForm(form) {\n        const formData = new FormData(form);\n        const data = {};\n\n        formData.forEach((value, key) => {\n            const input = formData.get(key);\n\n            if (input.type === \"checkbox\") {\n                data[key] = input.checked;\n            } else if (input.type === \"number\") {\n                data[key] = parseFloat(value + \"\");\n            } else {\n                data[key] = value;\n            }\n        });\n\n        return data\n    }\n\n    function getFlowPath() {\n        const optionsEl = document.getElementById(\"options\");\n        const selectedIndex = optionsEl.selectedIndex;\n        return \"/\" + optionsEl.options[selectedIndex].value\n    }\n\n    function updateFlow(event, form, csrfToken) {\n        event.preventDefault();\n        const data = serializeForm(form)\n\n        fetch(form.action, {\n            headers: {\n                \"Content-Type\": \"application/json\"\n            },\n            method: form.method,\n            body: JSON.stringify({\n              input_data: data,\n              csrf_token: csrfToken\n            }),\n        })\n            .then(response => response.json())\n            .then((data) => {\n              if (data.name === 'thirdparty_oauth') {\n                localStorage.setItem('thirdparty_oauth_state', JSON.stringify(data))\n              }\n              return data\n            })\n            .then(generateUI)\n            .catch(console.error)\n            .finally(resetAssertionResponseInput);\n    }\n\n    function createFlow() {\n        fetch(getFlowPath(), {\n            method: \"POST\",\n            headers: {\n                \"Content-Type\": \"application/json\"\n            },\n            body: JSON.stringify({flow_option: getFlowPath()})\n        })\n            .then((resp) => resp.json())\n            .then(generateUI)\n            .catch(console.error)\n    }\n\n    function init() {\n        const newLoginFlowBtnEL = document.getElementById(\"newLoginFlowBtn\");\n        newLoginFlowBtnEL.addEventListener(\"click\", () => {\n            createFlow();\n        })\n    }\n\n    init();\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "backend/flowpilot/action_input.go",
    "content": "package flowpilot\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot/jsonmanager\"\n)\n\ntype actionInput interface {\n\tjsonmanager.JSONManager\n}\n\ntype readOnlyActionInput interface {\n\tjsonmanager.ReadOnlyJSONManager\n}\n\n// newActionInput creates a new instance of actionInput with empty JSON data.\nfunc newActionInput() actionInput {\n\treturn jsonmanager.NewJSONManager()\n}\n\n// newActionInputFromInputData creates a new instance of actionInput with the given JSON data\n// which was previously unmarshalled into a generic map.\nfunc newActionInputFromInputData(data InputData) (actionInput, error) {\n\tdataBytes, err := json.Marshal(data.InputDataMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn jsonmanager.NewJSONManagerFromString(string(dataBytes))\n}\n"
  },
  {
    "path": "backend/flowpilot/builder.go",
    "content": "package flowpilot\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n)\n\ntype FlowBuilder interface {\n\tTTL(ttl time.Duration) FlowBuilder\n\tState(stateName StateName, actions ...Action) FlowBuilder\n\tInitialState(stateNames ...StateName) FlowBuilder\n\tErrorState(stateName StateName) FlowBuilder\n\tBeforeState(stateName StateName, hooks ...HookAction) FlowBuilder\n\tAfterState(stateName StateName, hooks ...HookAction) FlowBuilder\n\tAfterFlow(flowName FlowName, hooks ...HookAction) FlowBuilder\n\tDebug(enabled bool) FlowBuilder\n\tSubFlows(subFlows ...subFlow) FlowBuilder\n\tBuild() (Flow, error)\n\tMustBuild() Flow\n\tBeforeEachAction(hooks ...HookAction) FlowBuilder\n\tAfterEachAction(hooks ...HookAction) FlowBuilder\n}\n\n// defaultFlowBuilderBase is the base flow builder struct.\ntype defaultFlowBuilderBase struct {\n\tname                  FlowName\n\tflow                  stateActions\n\tsubFlows              SubFlows\n\tstateDetails          stateDetails\n\tbeforeStateHooks      stateHooks\n\tafterStateHooks       stateHooks\n\tbeforeEachActionHooks hookActions\n\tafterEachActionHooks  hookActions\n\tafterFlowHooks        flowHooks\n}\n\n// defaultFlowBuilder is a builder struct for creating a new Flow.\ntype defaultFlowBuilder struct {\n\tpath              string\n\tttl               time.Duration\n\tinitialStateNames []StateName\n\terrorStateName    StateName\n\tdebug             bool\n\n\tdefaultFlowBuilderBase\n}\n\n// newFlowBuilderBase creates a new defaultFlowBuilderBase instance.\nfunc newFlowBuilderBase(name FlowName) defaultFlowBuilderBase {\n\treturn defaultFlowBuilderBase{\n\t\tname:             name,\n\t\tflow:             make(stateActions),\n\t\tsubFlows:         make(SubFlows, 0),\n\t\tstateDetails:     make(stateDetails),\n\t\tbeforeStateHooks: make(stateHooks),\n\t\tafterStateHooks:  make(stateHooks),\n\t\tafterFlowHooks:   make(flowHooks),\n\t}\n}\n\n// NewFlow creates a new defaultFlowBuilder that builds a new flow available under the specified path.\nfunc NewFlow(name FlowName) FlowBuilder {\n\tpath := fmt.Sprintf(\"/%s\", name)\n\tfbBase := newFlowBuilderBase(name)\n\treturn &defaultFlowBuilder{path: path, defaultFlowBuilderBase: fbBase}\n}\n\n// TTL sets the time-to-live (TTL) for the flow.\nfunc (fb *defaultFlowBuilder) TTL(ttl time.Duration) FlowBuilder {\n\tfb.ttl = ttl\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilderBase) addState(stateName StateName, actions ...Action) {\n\tfb.flow[stateName] = append(fb.flow[stateName], actions...)\n}\n\nfunc (fb *defaultFlowBuilderBase) addBeforeStateHooks(stateName StateName, hooks ...HookAction) {\n\tfb.beforeStateHooks[stateName] = append(fb.beforeStateHooks[stateName], hooks...)\n}\n\nfunc (fb *defaultFlowBuilderBase) addAfterStateHooks(stateName StateName, hooks ...HookAction) {\n\tfb.afterStateHooks[stateName] = append(fb.afterStateHooks[stateName], hooks...)\n}\n\nfunc (fb *defaultFlowBuilderBase) addAfterFlowHooks(flowName FlowName, hooks ...HookAction) {\n\tfb.afterFlowHooks[flowName] = append(fb.afterFlowHooks[flowName], hooks...)\n}\n\nfunc (fb *defaultFlowBuilder) addBeforeEachActionHooks(hooks ...HookAction) {\n\tfb.beforeEachActionHooks = append(fb.beforeEachActionHooks, hooks...)\n}\n\nfunc (fb *defaultFlowBuilder) addAfterEachActionHooks(hooks ...HookAction) {\n\tfb.afterEachActionHooks = append(fb.afterEachActionHooks, hooks...)\n}\n\nfunc (fb *defaultFlowBuilderBase) addSubFlows(subFlows ...subFlow) {\n\tfb.subFlows = append(fb.subFlows, subFlows...)\n}\n\nfunc (fb *defaultFlowBuilderBase) addStateIfNotExists(stateName StateName) {\n\tif _, exists := fb.flow[stateName]; !exists {\n\t\tfb.addState(stateName)\n\t}\n}\n\n// scanFlowStates iterates through each state in the provided flow and associates relevant information, also it checks\n// for uniqueness of state names.\nfunc (fb *defaultFlowBuilder) scanFlowStates(flow flowBase) error {\n\t// Iterate through states in the flow.\n\tfor stateName, actions := range flow.getFlow() {\n\t\t// Check if state name is already in use.\n\t\tif _, ok := fb.stateDetails[stateName]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tactionDetails := make(defaultActionDetails, len(actions))\n\n\t\tfor i, action := range actions {\n\t\t\tactionDetails[i] = &defaultActionDetail{\n\t\t\t\taction:   action,\n\t\t\t\tflowName: flow.getName(),\n\t\t\t}\n\t\t}\n\n\t\t// Create state details.\n\t\tstate := &defaultStateDetail{\n\t\t\tname:          stateName,\n\t\t\tactionDetails: actionDetails,\n\t\t\tflow:          flow.getFlow(),\n\t\t\tsubFlows:      flow.getSubFlows(),\n\t\t\tflowName:      flow.getName(),\n\t\t}\n\n\t\t// Store state details.\n\t\tfb.stateDetails[stateName] = state\n\t}\n\n\tfor stateName, actions := range flow.getBeforeStateHooks() {\n\t\tfb.beforeStateHooks[stateName] = append(fb.beforeStateHooks[stateName], actions...)\n\t}\n\n\tfor stateName, actions := range flow.getAfterStateHooks() {\n\t\tfb.afterStateHooks[stateName] = append(fb.afterStateHooks[stateName], actions...)\n\t}\n\n\tactions := flow.getAfterFlowHooks()\n\tfb.afterFlowHooks[flow.getName()] = append(fb.afterFlowHooks[flow.getName()], actions...)\n\n\t// Recursively scan sub-flows.\n\tfor _, sf := range flow.getSubFlows() {\n\t\tif err := fb.scanFlowStates(sf); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// validate performs validation checks on the flow configuration.\nfunc (fb *defaultFlowBuilder) validate() error {\n\t// Validate fixed states and their presence in the flow.\n\tif len(fb.initialStateNames) == 0 {\n\t\treturn errors.New(\"fixed state 'initialState' is not set\")\n\t}\n\tif len(fb.errorStateName) == 0 {\n\t\treturn errors.New(\"fixed state 'errorState' is not set\")\n\t}\n\tif !fb.flow.stateExists(fb.initialStateNames[0]) && !fb.subFlows.stateExists(fb.initialStateNames[0]) {\n\t\treturn fmt.Errorf(\"initial state '%s' does not belong to the flow or a sub-flow\", fb.initialStateNames[0])\n\t}\n\tif !fb.flow.stateExists(fb.errorStateName) {\n\t\treturn fmt.Errorf(\"error state '%s' does not belong to the flow\", fb.errorStateName)\n\t}\n\n\treturn nil\n}\n\n// State adds a new state to the flow.\nfunc (fb *defaultFlowBuilder) State(stateName StateName, actions ...Action) FlowBuilder {\n\tfb.addState(stateName, actions...)\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) BeforeState(stateName StateName, hooks ...HookAction) FlowBuilder {\n\tfb.addBeforeStateHooks(stateName, hooks...)\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) AfterState(stateName StateName, hooks ...HookAction) FlowBuilder {\n\tfb.addAfterStateHooks(stateName, hooks...)\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) AfterFlow(flowName FlowName, hooks ...HookAction) FlowBuilder {\n\tfb.addAfterFlowHooks(flowName, hooks...)\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) BeforeEachAction(hooks ...HookAction) FlowBuilder {\n\tfb.addBeforeEachActionHooks(hooks...)\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) AfterEachAction(hooks ...HookAction) FlowBuilder {\n\tfb.addAfterEachActionHooks(hooks...)\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) InitialState(nextStateNames ...StateName) FlowBuilder {\n\tfb.initialStateNames = nextStateNames\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) ErrorState(stateName StateName) FlowBuilder {\n\tfb.addStateIfNotExists(stateName)\n\tfb.errorStateName = stateName\n\treturn fb\n}\n\nfunc (fb *defaultFlowBuilder) SubFlows(subFlows ...subFlow) FlowBuilder {\n\tfb.addSubFlows(subFlows...)\n\treturn fb\n}\n\n// Debug enables the debug mode, which causes the flow response to contain the actual error.\nfunc (fb *defaultFlowBuilder) Debug(enabled bool) FlowBuilder {\n\tfb.debug = enabled\n\treturn fb\n}\n\n// Build constructs and returns the Flow object.\nfunc (fb *defaultFlowBuilder) Build() (Flow, error) {\n\tif err := fb.validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"flow validation failed: %w\", err)\n\t}\n\n\tdfb := &defaultFlowBase{\n\t\tname:                  fb.name,\n\t\tflow:                  fb.flow,\n\t\tsubFlows:              fb.subFlows,\n\t\tbeforeStateHooks:      fb.beforeStateHooks,\n\t\tafterStateHooks:       fb.afterStateHooks,\n\t\tbeforeEachActionHooks: fb.beforeEachActionHooks,\n\t\tafterEachActionHooks:  fb.afterEachActionHooks,\n\t\tafterFlowHooks:        fb.afterFlowHooks,\n\t}\n\n\tflow := &defaultFlow{\n\t\tinitialStateNames: fb.initialStateNames,\n\t\terrorStateName:    fb.errorStateName,\n\t\tstateDetails:      fb.stateDetails,\n\t\tttl:               fb.ttl,\n\t\tdebug:             fb.debug,\n\t\tdefaultFlowBase:   dfb,\n\t\tcontextValues:     make(contextValues),\n\t}\n\n\t// Check if states were already scanned, if so, don't scan again\n\tif len(fb.stateDetails) == 0 {\n\t\tif err := fb.scanFlowStates(flow); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to scan flow states: %w\", err)\n\t\t}\n\t}\n\n\tflow.defaultFlowBase.beforeStateHooks.makeUnique()\n\tflow.defaultFlowBase.afterStateHooks.makeUnique()\n\tflow.defaultFlowBase.afterFlowHooks.makeUnique()\n\n\treturn flow, nil\n}\n\n// MustBuild constructs and returns the Flow object, panics on error.\nfunc (fb *defaultFlowBuilder) MustBuild() Flow {\n\tf, err := fb.Build()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn f\n}\n"
  },
  {
    "path": "backend/flowpilot/builder_subflow.go",
    "content": "package flowpilot\n\ntype SubFlowBuilder interface {\n\tState(stateName StateName, actions ...Action) SubFlowBuilder\n\tBeforeState(stateName StateName, hooks ...HookAction) SubFlowBuilder\n\tAfterState(stateName StateName, hooks ...HookAction) SubFlowBuilder\n\tSubFlows(subFlows ...subFlow) SubFlowBuilder\n\tBuild() (subFlow, error)\n\tMustBuild() subFlow\n}\n\n// defaultFlowBuilder is a builder struct for creating a new subFlow.\ntype defaultSubFlowBuilder struct {\n\tdefaultFlowBuilderBase\n}\n\n// NewSubFlow creates a new SubFlowBuilder.\nfunc NewSubFlow(name FlowName) SubFlowBuilder {\n\tfbBase := newFlowBuilderBase(name)\n\treturn &defaultSubFlowBuilder{defaultFlowBuilderBase: fbBase}\n}\n\nfunc (sfb *defaultSubFlowBuilder) SubFlows(subFlows ...subFlow) SubFlowBuilder {\n\tsfb.addSubFlows(subFlows...)\n\treturn sfb\n}\n\n// State adds a new state to the flow.\nfunc (sfb *defaultSubFlowBuilder) State(stateName StateName, actions ...Action) SubFlowBuilder {\n\tsfb.addState(stateName, actions...)\n\treturn sfb\n}\n\nfunc (sfb *defaultSubFlowBuilder) BeforeState(stateName StateName, hooks ...HookAction) SubFlowBuilder {\n\tsfb.addBeforeStateHooks(stateName, hooks...)\n\treturn sfb\n}\n\nfunc (sfb *defaultSubFlowBuilder) AfterState(stateName StateName, hooks ...HookAction) SubFlowBuilder {\n\tsfb.addAfterStateHooks(stateName, hooks...)\n\treturn sfb\n}\n\n// Build constructs and returns the subFlow object.\nfunc (sfb *defaultSubFlowBuilder) Build() (subFlow, error) {\n\tf := defaultFlowBase{\n\t\tname:             sfb.name,\n\t\tflow:             sfb.flow,\n\t\tsubFlows:         sfb.subFlows,\n\t\tbeforeStateHooks: sfb.beforeStateHooks,\n\t\tafterStateHooks:  sfb.afterStateHooks,\n\t}\n\n\treturn &f, nil\n}\n\n// MustBuild constructs and returns the subFlow object, panics on error.\nfunc (sfb *defaultSubFlowBuilder) MustBuild() subFlow {\n\tsf, err := sfb.Build()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn sf\n}\n"
  },
  {
    "path": "backend/flowpilot/context.go",
    "content": "package flowpilot\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype context interface {\n\t// Get returns the context value with the given name.\n\tGet(string) interface{}\n\tGetFlowName() FlowName\n\t// IsFlow returns true if the name matches the current flow name.\n\tIsFlow(name FlowName) bool\n}\n\n// flowContext represents the basic context for a flow.\ntype flowContext interface {\n\t// Set sets a context value for the given key.\n\tSet(string, interface{})\n\t// GetFlowID returns the unique ID of the current defaultFlow.\n\tGetFlowID() uuid.UUID\n\t// Payload returns the JSONManager for accessing payload data.\n\tPayload() payload\n\t// Stash returns the JSONManager for accessing stash data.\n\tStash() stash\n\t// GetInitialState returns the initial state of the flow.\n\tGetInitialState() StateName\n\t// GetCurrentState returns the current state of the flow.\n\tGetCurrentState() StateName\n\tIsStateScheduled(StateName) bool\n\tStateVisited(name StateName) bool\n\tGetScheduledStates() []StateName\n\t// GetPreviousState returns the previous state of the flow.\n\tGetPreviousState() StateName\n\t// IsPreviousState returns true if the previous state equals the given name.\n\tIsPreviousState(name StateName) bool\n\t// GetErrorState returns the designated error state of the flow.\n\tGetErrorState() StateName\n}\n\n// actionInitializationContext represents the basic context for a flow action's initialization.\ntype actionInitializationContext interface {\n\t// AddInputs adds input parameters to the inputSchema.\n\tAddInputs(inputs ...Input)\n\tStateIsRevertible() bool\n\n\tflowContext\n\tactionSuspender\n}\n\n// actionExecutionContext represents the context for an action execution.\ntype actionExecutionContext interface {\n\t// Input returns the executionInputSchema for the action.\n\tInput() executionInputSchema\n\t// ValidateInputData validates the input data against the inputSchema.\n\tValidateInputData() bool\n\t// CopyInputValuesToStash copies specified inputs to the stash.\n\tCopyInputValuesToStash(inputNames ...string) error\n\tSetFlowError(FlowError)\n\tPreventRevert()\n\tExecuteHook(HookAction) error\n\tactionSuspender\n\tflowContext\n}\n\n// actionExecutionContinuationContext represents the context within an action continuation.\ntype actionExecutionContinuationContext interface {\n\tContinue(stateNames ...StateName) error\n\t// Error continues the flow execution to the specified next state with an error.\n\tError(flowErr FlowError) error\n\t// Revert reverts the flow back to the previous state.\n\tRevert() error\n\n\tactionExecutionContext\n}\n\ntype actionSuspender interface {\n\t// SuspendAction suspends the current action's execution.\n\tSuspendAction()\n}\n\ntype Context interface {\n\tcontext\n}\n\n// InitializationContext is a shorthand for actionInitializationContext within the flow initialization method.\ntype InitializationContext interface {\n\tcontext\n\tactionInitializationContext\n}\n\n// ExecutionContext is a shorthand for actionExecutionContinuationContext within flow execution method.\ntype ExecutionContext interface {\n\tcontext\n\tactionExecutionContinuationContext\n}\n\ntype HookExecutionContext interface {\n\tcontext\n\tactionExecutionContext\n\n\tGetFlowError() FlowError\n\tAddLink(...Link)\n\tScheduleStates(...StateName)\n}\n\ntype BeforeEachActionExecutionContext interface {\n\tactionExecutionContinuationContext\n}\n\n// createAndInitializeFlow initializes the flow and returns a flow Response.\nfunc createAndInitializeFlow(db FlowDB, flow defaultFlow) (FlowResult, error) {\n\t// Wrap the provided FlowDB with additional functionality.\n\tdbw := wrapDB(db)\n\t// Calculate the expiration time for the flow.\n\texpiresAt := time.Now().Add(flow.ttl).UTC()\n\n\t// Initialize the stash and add initial next states.\n\ts, err := newStash(flow.initialStateNames...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to initialize a new stash: %w\", err)\n\t}\n\n\ts.useCompression(flow.useCompression)\n\n\tp := newPayload()\n\n\tcsrfToken, err := generateRandomString(32)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to generate csrf token: %w\", err)\n\t}\n\n\t// Create a new flow model with the provided parameters.\n\tflowCreation := flowCreationParam{\n\t\tdata:      s.String(),\n\t\tcsrfToken: csrfToken,\n\t\texpiresAt: expiresAt,\n\t}\n\tflowModel, err := dbw.createFlowWithParam(flowCreation)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create flow: %w\", err)\n\t}\n\n\t// Create a defaultFlowContext instance.\n\tfc := &defaultFlowContext{\n\t\tflow:      flow,\n\t\tdbw:       dbw,\n\t\tflowModel: flowModel,\n\t\tstash:     s,\n\t\tpayload:   p,\n\t}\n\n\ter := executionResult{nextStateName: s.getStateName()}\n\n\taec := defaultActionExecutionContext{\n\t\tactionName:         \"\",\n\t\tinputSchema:        nil,\n\t\texecutionResult:    &er,\n\t\tdefaultFlowContext: fc,\n\t}\n\n\terr = aec.executeBeforeStateHooks(aec.stash.getStateName())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to execute before hook actions: %w\", err)\n\t}\n\n\treturn er.generateResponse(fc), nil\n}\n\n// executeFlowAction processes the flow and returns a Response.\nfunc executeFlowAction(db FlowDB, flow defaultFlow) (FlowResult, error) {\n\tactionName := flow.queryParam.getActionName()\n\n\t// Retrieve the flow model from the database using the flow ID.\n\tflowModel, err := db.GetFlow(flow.queryParam.getFlowID())\n\tif err != nil {\n\t\tif errors.Is(err, sql.ErrNoRows) {\n\t\t\treturn newFlowResultFromError(flow.errorStateName, ErrorOperationNotPermitted.Wrap(err), flow.debug), nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to get flow: %w\", err)\n\t}\n\n\t// Check if the flow has expired.\n\tif time.Now().After(flowModel.ExpiresAt) {\n\t\treturn newFlowResultFromError(flow.errorStateName, ErrorFlowExpired, flow.debug), nil\n\t}\n\n\t// Parse stash data from the flow model.\n\ts, err := newStashFromString(flowModel.Data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse stash from flow: %w\", err)\n\t}\n\n\ts.useCompression(flow.useCompression)\n\n\t// Initialize JSONManagers for payload and flash data.\n\tp := newPayload()\n\n\t// Parse raw input data into JSONManager.\n\tinputJSON, err := newActionInputFromInputData(flow.inputData)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse input data: %w\", err)\n\t}\n\tcsrfTokenToValidate := flow.inputData.CSRFToken\n\n\tif len(flowModel.CSRFToken) <= 0 || flowModel.CSRFToken != csrfTokenToValidate {\n\t\terr = errors.New(\"csrf token mismatch\")\n\t\treturn newFlowResultFromError(flow.errorStateName, ErrorOperationNotPermitted.Wrap(err), flow.debug), nil\n\t}\n\n\t// Create a defaultFlowContext instance.\n\tfc := &defaultFlowContext{\n\t\tflow:      flow,\n\t\tdbw:       wrapDB(db),\n\t\tflowModel: flowModel,\n\t\tstash:     s,\n\t\tpayload:   p,\n\t}\n\n\tstate, err := flow.getState(s.getStateName())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Get the action associated with the actionParam name.\n\tad, err := state.getActionDetail(actionName)\n\tif err != nil {\n\t\treturn newFlowResultFromError(flow.errorStateName, ErrorOperationNotPermitted.Wrap(err), flow.debug), nil\n\t}\n\n\t// Initialize the inputSchema and action context for action execution.\n\tinputSchema := newSchemaWithInputData(inputJSON)\n\taic := &defaultActionInitializationContext{\n\t\tinputSchema:        inputSchema.forInitializationContext(),\n\t\tdefaultFlowContext: fc,\n\t}\n\n\t// Create a actionExecutionContext instance for action execution.\n\taec := &defaultActionExecutionContext{\n\t\tactionName:         actionName,\n\t\tinputSchema:        inputSchema,\n\t\tdefaultFlowContext: fc,\n\t}\n\n\terr = aec.executeBeforeEachActionHooks()\n\tif err != nil {\n\t\treturn newFlowResultFromError(flow.errorStateName, ErrorOperationNotPermitted, flow.debug), nil\n\t}\n\n\tad.getAction().Initialize(aic)\n\n\t// Check if the action is suspended.\n\tif aic.isSuspended {\n\t\treturn newFlowResultFromError(flow.errorStateName, ErrorOperationNotPermitted, flow.debug), nil\n\t}\n\n\t// Execute the action and handle any errors.\n\terr = ad.getAction().Execute(aec)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"the action failed to handle the request: %w\", err)\n\t}\n\n\t// Ensure that the action has set a result object.\n\tif aec.executionResult == nil {\n\t\ter := executionResult{nextStateName: s.getStateName()}\n\t\taec.executionResult = &er\n\t}\n\n\t// Generate a response based on the execution result.\n\treturn aec.executionResult.generateResponse(fc), nil\n}\n"
  },
  {
    "path": "backend/flowpilot/context_action_exec.go",
    "content": "package flowpilot\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// defaultActionExecutionContext is the default implementation of the actionExecutionContext interface.\ntype defaultActionExecutionContext struct {\n\tactionName      ActionName           // Name of the action being executed.\n\tinputSchema     executionInputSchema // JSONManager for accessing input data.\n\tflowError       FlowError\n\texecutionResult *executionResult // Result of the action execution.\n\tlinks           []Link           // TODO:\n\tisSuspended     bool\n\tpreventRevert   bool\n\n\t*defaultFlowContext // Embedding the defaultFlowContext for common context fields.\n}\n\n// closeExecutionContext updates the flow's state and stores data to the database.\nfunc (aec *defaultActionExecutionContext) closeExecutionContext() error {\n\tvar err error\n\n\tif aec.executionResult != nil {\n\t\treturn errors.New(\"execution context is closed already\")\n\t}\n\n\tnextStateName := aec.stash.getStateName()\n\n\tactionResult := &actionExecutionResult{\n\t\tactionName:  aec.actionName,\n\t\tinputSchema: aec.inputSchema,\n\t\tisSuspended: aec.isSuspended,\n\t}\n\n\tresult := &executionResult{\n\t\tflowError:             aec.flowError,\n\t\tactionExecutionResult: actionResult,\n\t\tlinks:                 aec.links,\n\t\tnextStateName:         nextStateName,\n\t}\n\n\taec.executionResult = result\n\n\tcsrfToken, err := generateRandomString(32)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate csrf token: %w\", err)\n\t}\n\n\tnewVersion := aec.flowModel.Version + 1\n\n\t// Prepare parameters for updating the flow in the database.\n\tflowUpdate := flowUpdateParam{\n\t\tflowID:    aec.flowModel.ID,\n\t\tdata:      aec.stash.String(),\n\t\tversion:   newVersion,\n\t\tcsrfToken: csrfToken,\n\t\texpiresAt: aec.flowModel.ExpiresAt,\n\t\tcreatedAt: aec.flowModel.CreatedAt,\n\t}\n\n\t// Update the flow model in the database.\n\tif _, err = aec.dbw.updateFlowWithParam(flowUpdate); err != nil {\n\t\treturn fmt.Errorf(\"failed to store updated flow: %w\", err)\n\t}\n\n\taec.flowModel.Version = newVersion\n\taec.flowModel.CSRFToken = csrfToken\n\n\treturn nil\n}\n\nfunc (aec *defaultActionExecutionContext) executeBeforeStateHooks(nextStateName StateName) error {\n\tif actions := aec.flow.beforeStateHooks[nextStateName]; actions != nil {\n\t\tfor _, hook := range actions.reverse() {\n\t\t\tif err := hook.Execute(aec); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to execute before state hook (state: %s): %w\", nextStateName, err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (aec *defaultActionExecutionContext) executeBeforeEachActionHooks() error {\n\tfor _, hook := range aec.flow.beforeEachActionHooks {\n\t\terr := hook.Execute(aec)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to execute before each action (action: %s): %w\", aec.actionName, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (aec *defaultActionExecutionContext) executeAfterHooks() error {\n\tcurrentStateName := aec.stash.getStateName()\n\tcurrentState, _ := aec.flow.getState(currentStateName)\n\tcurrentFlowName := currentState.getFlowName()\n\n\tvar nextFlowName FlowName\n\tif nextStateName := aec.stash.getNextStateName(); len(nextStateName) > 0 {\n\t\tnextState, _ := aec.flow.getState(nextStateName)\n\t\tnextFlowName = nextState.getFlowName()\n\t}\n\n\tif len(nextFlowName) == 0 || currentFlowName != nextFlowName {\n\t\tfor _, hook := range aec.flow.afterFlowHooks[currentFlowName].reverse() {\n\t\t\tif err := hook.Execute(aec); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to execute hook after flow hook (flow: %s): %w\", currentFlowName, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif actions := aec.flow.afterStateHooks[currentStateName]; actions != nil {\n\t\tfor _, hook := range actions.reverse() {\n\t\t\tif err := hook.Execute(aec); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to execute after state hook (state: %s): %w\", currentStateName, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, hook := range aec.flow.afterEachActionHooks {\n\t\terr := hook.Execute(aec)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to execute after each action hook (action: %s): %w\", aec.actionName, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Input returns the executionInputSchema for accessing input data.\nfunc (aec *defaultActionExecutionContext) Input() executionInputSchema {\n\treturn aec.inputSchema\n}\n\n// payload returns the JSONManager for accessing payload data.\nfunc (aec *defaultActionExecutionContext) Payload() payload {\n\treturn aec.payload\n}\n\n// CopyInputValuesToStash copies specified inputs to the stash.\nfunc (aec *defaultActionExecutionContext) CopyInputValuesToStash(inputNames ...string) error {\n\tfor _, inputName := range inputNames {\n\t\t// Copy input values to the stash.\n\t\tif result := aec.inputSchema.Get(inputName); result.Exists() && len(result.String()) > 0 {\n\t\t\tif err := aec.stash.Set(inputName, result.Value()); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (aec *defaultActionExecutionContext) SetFlowError(err FlowError) {\n\taec.flowError = err\n}\n\nfunc (aec *defaultActionExecutionContext) GetFlowError() FlowError {\n\treturn aec.flowError\n}\n\n// ValidateInputData validates the input data against the inputSchema.\nfunc (aec *defaultActionExecutionContext) ValidateInputData() bool {\n\treturn aec.inputSchema.validateInputData()\n}\n\n// Error continues the flow execution to the current state, if it's a 4xx error or to the error state otherwise.\n// The flow response will contain the given error.\nfunc (aec *defaultActionExecutionContext) Error(flowErr FlowError) error {\n\taec.flowError = flowErr\n\tstatusStr := strconv.Itoa(aec.flowError.Status())\n\n\tvar nextStateName StateName\n\tif strings.HasPrefix(statusStr, \"4\") {\n\t\tnextStateName = aec.stash.getStateName()\n\t} else {\n\t\tnextStateName = aec.flow.errorStateName\n\t}\n\n\tif err := aec.executeBeforeStateHooks(nextStateName); err != nil {\n\t\treturn err\n\t}\n\n\tif err := aec.stash.pushErrorState(nextStateName); err != nil {\n\t\treturn err\n\t}\n\n\treturn aec.closeExecutionContext()\n}\n\n// Revert reverts the flow back to the previous state.\nfunc (aec *defaultActionExecutionContext) Revert() error {\n\tif err := aec.stash.revertState(); err != nil {\n\t\treturn fmt.Errorf(\"failed to revert to the previous state: %w\", err)\n\t}\n\n\tif err := aec.executeBeforeEachActionHooks(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := aec.executeBeforeStateHooks(aec.stash.getStateName()); err != nil {\n\t\treturn err\n\t}\n\n\treturn aec.closeExecutionContext()\n}\n\nfunc (aec *defaultActionExecutionContext) Continue(stateNames ...StateName) error {\n\tfor _, stateName := range stateNames {\n\t\tif _, ok := aec.flow.stateDetails[stateName]; !ok {\n\t\t\treturn fmt.Errorf(\"cannot continue, state does not exist: %s\", stateName)\n\t\t}\n\t}\n\n\tif err := aec.executeBeforeEachActionHooks(); err != nil {\n\t\treturn err\n\t}\n\n\taec.stash.addScheduledStateNames(stateNames...)\n\n\tif err := aec.executeAfterHooks(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := aec.executeBeforeStateHooks(aec.stash.getNextStateName()); err != nil {\n\t\treturn err\n\t}\n\n\tif err := aec.stash.pushState(!aec.preventRevert); err != nil {\n\t\treturn fmt.Errorf(\"cannot continue, failed to update stash data: %s\", err)\n\t}\n\n\treturn aec.closeExecutionContext()\n}\n\nfunc (aec *defaultActionExecutionContext) AddLink(links ...Link) {\n\taec.links = append(aec.links, links...)\n}\n\nfunc (aec *defaultActionExecutionContext) ScheduleStates(stateNames ...StateName) {\n\taec.stash.addScheduledStateNames(stateNames...)\n}\n\nfunc (aec *defaultActionExecutionContext) Set(key string, value interface{}) {\n\taec.flow.Set(key, value)\n}\n\nfunc (aec *defaultActionExecutionContext) SuspendAction() {\n\taec.isSuspended = true\n}\n\nfunc (aec *defaultActionExecutionContext) PreventRevert() {\n\taec.preventRevert = true\n}\n\nfunc (aec *defaultActionExecutionContext) ExecuteHook(a HookAction) error {\n\treturn a.Execute(aec)\n}\n"
  },
  {
    "path": "backend/flowpilot/context_action_init.go",
    "content": "package flowpilot\n\n// defaultActionInitializationContext is the default implementation of the actionInitializationContext interface.\ntype defaultActionInitializationContext struct {\n\tinputSchema         initializationInputSchema // initializationInputSchema for action initialization.\n\tisSuspended         bool                      // Flag indicating if the method is suspended.\n\t*defaultFlowContext                           // Embedding the defaultFlowContext for common context fields.\n}\n\nfunc (aic *defaultActionInitializationContext) Payload() payload {\n\treturn aic.payload\n}\n\nfunc (aic *defaultActionInitializationContext) Set(s string, i interface{}) {\n\taic.flow.Set(s, i)\n}\n\n// AddInputs adds input data to the initializationInputSchema.\nfunc (aic *defaultActionInitializationContext) AddInputs(inputs ...Input) {\n\taic.inputSchema.AddInputs(inputs...)\n}\n\n// SuspendAction sets the isSuspended flag to indicate the action is suspended.\nfunc (aic *defaultActionInitializationContext) SuspendAction() {\n\taic.isSuspended = true\n}\n\n// Stash returns the ReadOnlyJSONManager for accessing stash data.\nfunc (aic *defaultActionInitializationContext) Stash() stash {\n\treturn aic.stash\n}\n\n// Get returns the context value with the given name.\nfunc (aic *defaultActionInitializationContext) Get(key string) interface{} {\n\treturn aic.flow.contextValues[key]\n}\n\nfunc (aic *defaultActionInitializationContext) StateIsRevertible() bool {\n\treturn aic.stash.isRevertible()\n}\n"
  },
  {
    "path": "backend/flowpilot/context_flow.go",
    "content": "package flowpilot\n\nimport (\n\t\"github.com/gofrs/uuid\"\n)\n\n// defaultFlowContext is the default implementation of the flowContext interface.\ntype defaultFlowContext struct {\n\tpayload   payload       // JSONManager for payload data.\n\tstash     stash         // JSONManager for stash data.\n\tflow      defaultFlow   // The associated defaultFlow instance.\n\tdbw       flowDBWrapper // Wrapped FlowDB instance with additional functionality.\n\tflowModel *FlowModel    // The current FlowModel.\n}\n\n// GetFlowID returns the unique ID of the current flow.\nfunc (fc *defaultFlowContext) GetFlowID() uuid.UUID {\n\treturn fc.flowModel.ID\n}\n\n// GetInitialState returns the initial state of the flow.\nfunc (fc *defaultFlowContext) GetInitialState() StateName {\n\treturn fc.flow.initialStateNames[0]\n}\n\n// GetCurrentState returns the current state of the flow.\nfunc (fc *defaultFlowContext) GetCurrentState() StateName {\n\treturn fc.stash.getStateName()\n}\n\nfunc (fc *defaultFlowContext) GetScheduledStates() []StateName {\n\treturn fc.stash.getScheduledStateNames()\n}\n\n// CurrentStateEquals returns true, when one of the given stateNames matches the current state name.\nfunc (fc *defaultFlowContext) CurrentStateEquals(stateNames ...StateName) bool {\n\tfor _, s := range stateNames {\n\t\tif s == fc.stash.getStateName() {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (fc *defaultFlowContext) IsStateScheduled(name StateName) bool {\n\tfor _, state := range fc.stash.getScheduledStateNames() {\n\t\tif state == name {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (fc *defaultFlowContext) StateVisited(name StateName) bool {\n\treturn fc.stash.stateVisited(name)\n}\n\n// GetPreviousState returns the previous state of the flow.\nfunc (fc *defaultFlowContext) GetPreviousState() StateName {\n\treturn fc.stash.getPreviousStateName()\n}\n\n// IsPreviousState returns true if the previous state equals the given name\nfunc (fc *defaultFlowContext) IsPreviousState(name StateName) bool {\n\treturn fc.stash.getPreviousStateName() == name\n}\n\n// GetErrorState returns the designated error state of the flow.\nfunc (fc *defaultFlowContext) GetErrorState() StateName {\n\treturn fc.flow.errorStateName\n}\n\n// Stash returns the JSONManager for accessing stash data.\nfunc (fc *defaultFlowContext) Stash() stash {\n\treturn fc.stash\n}\n\n// Get returns the context value with the given name.\nfunc (fc *defaultFlowContext) Get(name string) interface{} {\n\treturn fc.flow.contextValues[name]\n}\n\n// GetFlowName returns the name of the current flow.\nfunc (fc *defaultFlowContext) GetFlowName() FlowName {\n\treturn fc.flow.name\n}\n\n// IsFlow returns true if the name matches the current flow name.\nfunc (fc *defaultFlowContext) IsFlow(name FlowName) bool {\n\treturn fc.flow.name == name\n}\n"
  },
  {
    "path": "backend/flowpilot/db.go",
    "content": "package flowpilot\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\n// FlowModel represents the database model for a flow.\ntype FlowModel struct {\n\tID        uuid.UUID // Unique ID of the flow.\n\tData      string    // Stash data associated with the flow.\n\tCSRFToken string    // Current CSRF token\n\tVersion   int       // Version of the flow.\n\tExpiresAt time.Time // Expiry time of the flow.\n\tCreatedAt time.Time // Creation time of the flow.\n\tUpdatedAt time.Time // Update time of the flow.\n}\n\n// FlowDB is the interface for interacting with the flow database.\ntype FlowDB interface {\n\tGetFlow(flowID uuid.UUID) (*FlowModel, error)\n\tCreateFlow(flowModel FlowModel) error\n\tUpdateFlow(flowModel FlowModel) error\n}\n\n// flowDBWrapper is an extended FlowDB interface that includes additional methods.\ntype flowDBWrapper interface {\n\tFlowDB\n\tcreateFlowWithParam(p flowCreationParam) (*FlowModel, error)\n\tupdateFlowWithParam(p flowUpdateParam) (*FlowModel, error)\n}\n\n// defaultFlowDBWrapper wraps a FlowDB instance to provide additional functionality.\ntype defaultFlowDBWrapper struct {\n\tFlowDB\n}\n\n// wrapDB wraps a FlowDB instance to provide flowDBWrapper functionality.\nfunc wrapDB(db FlowDB) flowDBWrapper {\n\treturn &defaultFlowDBWrapper{FlowDB: db}\n}\n\n// flowCreationParam holds parameters for creating a new flow.\ntype flowCreationParam struct {\n\tdata      string    //\n\tcsrfToken string    // Current CSRF token\n\texpiresAt time.Time // Expiry time of the flow.\n}\n\n// CreateFlowWithParam creates a new flow with the given parameters.\nfunc (w *defaultFlowDBWrapper) createFlowWithParam(p flowCreationParam) (*FlowModel, error) {\n\t// Generate a new UUID for the flow.\n\tflowID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to generate a new flow id: %w\", err)\n\t}\n\n\t// Prepare the FlowModel for creation.\n\tfm := FlowModel{\n\t\tID:        flowID,\n\t\tData:      p.data,\n\t\tVersion:   0,\n\t\tCSRFToken: p.csrfToken,\n\t\tExpiresAt: p.expiresAt,\n\t\tCreatedAt: time.Now().UTC(),\n\t\tUpdatedAt: time.Now().UTC(),\n\t}\n\n\t// Create the flow in the database.\n\terr = w.CreateFlow(fm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to store a new flow to the dbw: %w\", err)\n\t}\n\n\treturn &fm, nil\n}\n\n// flowUpdateParam holds parameters for updating a flow.\ntype flowUpdateParam struct {\n\tflowID    uuid.UUID // ID of the flow to update.\n\tdata      string    // Updated stash data for the flow.\n\tversion   int       // Updated version of the flow.\n\tcsrfToken string    // Current CSRF tokens\n\texpiresAt time.Time // Updated expiry time of the flow.\n\tcreatedAt time.Time // Original creation time of the flow.\n}\n\n// UpdateFlowWithParam updates the specified flow with the given parameters.\nfunc (w *defaultFlowDBWrapper) updateFlowWithParam(p flowUpdateParam) (*FlowModel, error) {\n\t// Prepare the updated FlowModel.\n\tfm := FlowModel{\n\t\tID:        p.flowID,\n\t\tData:      p.data,\n\t\tVersion:   p.version,\n\t\tCSRFToken: p.csrfToken,\n\t\tExpiresAt: p.expiresAt,\n\t\tUpdatedAt: time.Now().UTC(),\n\t\tCreatedAt: p.createdAt,\n\t}\n\n\t// Update the flow in the database.\n\terr := w.UpdateFlow(fm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to store updated flow to the dbw: %w\", err)\n\t}\n\n\treturn &fm, nil\n}\n"
  },
  {
    "path": "backend/flowpilot/errors.go",
    "content": "package flowpilot\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// flowpilotError defines the interface for custom error types in the Flowpilot package.\ntype flowpilotError interface {\n\terror\n\n\tUnwrap() error\n\tCode() string\n\tMessage() string\n\n\ttoResponseError(debug bool) *ResponseError\n}\n\n// FlowError is an interface representing flow-related errors.\ntype FlowError interface {\n\tflowpilotError\n\n\tWrap(error) FlowError\n\tStatus() int\n}\n\n// InputError is an interface representing input-related errors.\ntype InputError interface {\n\tflowpilotError\n\n\tWrap(error) InputError\n}\n\n// defaultError is a base struct for custom error types.\ntype defaultError struct {\n\tcause     error  // The error cause.\n\tcode      string // Unique error code.\n\tmessage   string // Contains a description of the error.\n\terrorText string // The string representation of the error.\n}\n\n// Code returns the error code.\nfunc (e *defaultError) Code() string {\n\treturn e.code\n}\n\n// Message returns the error message.\nfunc (e *defaultError) Message() string {\n\treturn e.message\n}\n\n// Unwrap returns the wrapped error.\nfunc (e *defaultError) Unwrap() error {\n\treturn e.cause\n}\n\n// Error returns the formatted error message.\nfunc (e *defaultError) Error() string {\n\treturn e.errorText\n}\n\n// toResponseError converts the error to a ResponseError for public exposure.\nfunc (e *defaultError) toResponseError(debug bool) *ResponseError {\n\tpublicError := &ResponseError{\n\t\tCode:    e.Code(),\n\t\tMessage: e.Message(),\n\t}\n\n\tif e.cause != nil {\n\t\tcause := e.cause.Error()\n\t\tpublicError.Internal = &cause\n\t\tif debug {\n\t\t\tpublicError.Cause = &cause\n\t\t}\n\t}\n\n\treturn publicError\n}\n\n// defaultFlowError is a struct for flow-related errors.\ntype defaultFlowError struct {\n\tdefaultError\n\n\tstatus int // The suggested HTTP status code.\n}\n\n// createErrorText creates the text used as the string representation of the error.\nfunc createErrorText(code, message string, cause error) string {\n\ttext := fmt.Sprintf(\"%s - %s\", code, message)\n\n\tif cause != nil {\n\t\ttext = fmt.Sprintf(\"%s: %s\", text, cause.Error())\n\t}\n\n\treturn text\n}\n\n// NewFlowError creates a new FlowError instance.\nfunc NewFlowError(code, message string, status int) FlowError {\n\treturn newFlowErrorWithCause(code, message, status, nil)\n}\n\n// newFlowErrorWithCause creates a new FlowError instance with an error cause.\nfunc newFlowErrorWithCause(code, message string, status int, cause error) FlowError {\n\terrorText := createErrorText(code, message, cause)\n\n\te := defaultError{\n\t\tcause:     cause,\n\t\tcode:      code,\n\t\tmessage:   message,\n\t\terrorText: errorText,\n\t}\n\n\treturn &defaultFlowError{defaultError: e, status: status}\n}\n\n// Status returns the suggested HTTP status code.\nfunc (e *defaultFlowError) Status() int {\n\treturn e.status\n}\n\n// Wrap wraps the error with another error.\nfunc (e *defaultFlowError) Wrap(err error) FlowError {\n\treturn newFlowErrorWithCause(e.code, e.message, e.status, err)\n}\n\n// defaultInputError is a struct for input-related errors.\ntype defaultInputError struct {\n\tdefaultError\n}\n\n// NewInputError creates a new InputError instance.\nfunc NewInputError(code, message string) InputError {\n\treturn newInputErrorWithCause(code, message, nil)\n}\n\n// newInputErrorWithCause creates a new InputError instance with an error cause.\nfunc newInputErrorWithCause(code, message string, cause error) InputError {\n\terrorText := createErrorText(code, message, cause)\n\n\te := defaultError{\n\t\tcause:     cause,\n\t\tcode:      code,\n\t\tmessage:   message,\n\t\terrorText: errorText,\n\t}\n\n\treturn &defaultInputError{defaultError: e}\n}\n\n// Wrap wraps the error with another error.\nfunc (e *defaultInputError) Wrap(err error) InputError {\n\treturn newInputErrorWithCause(e.code, e.message, err)\n}\n\n// Predefined flow error types\nvar (\n\tErrorTechnical             = NewFlowError(\"technical_error\", \"Something went wrong.\", http.StatusInternalServerError)\n\tErrorFlowExpired           = NewFlowError(\"flow_expired_error\", \"The flow has expired.\", http.StatusGone)\n\tErrorFlowDiscontinuity     = NewFlowError(\"flow_discontinuity_error\", \"The flow can't be continued.\", http.StatusInternalServerError)\n\tErrorOperationNotPermitted = NewFlowError(\"operation_not_permitted_error\", \"The operation is not permitted.\", http.StatusForbidden)\n\tErrorFormDataInvalid       = NewFlowError(\"form_data_invalid_error\", \"Form data invalid.\", http.StatusBadRequest)\n)\n\n// Predefined input error types\nvar (\n\tErrorValueMissing  = NewInputError(\"value_missing_error\", \"The value is missing.\")\n\tErrorValueInvalid  = NewInputError(\"value_invalid_error\", \"The value is invalid.\")\n\tErrorValueTooLong  = NewInputError(\"value_too_long_error\", \"The value is too long.\")\n\tErrorValueTooShort = NewInputError(\"value_too_short_error\", \"The value is too short.\")\n)\n\nfunc createMustBeOneOfError(values []string) InputError {\n\treturn NewInputError(\"value_invalid_error\", fmt.Sprintf(\"The value is invalid. Must be one of: %s\", strings.Join(values, \",\")))\n}\n"
  },
  {
    "path": "backend/flowpilot/flow.go",
    "content": "package flowpilot\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// FlowName represents the name of the flow.\ntype FlowName string\n\n// InputData holds input data in JSON format.\ntype InputData struct {\n\tInputDataMap map[string]interface{} `json:\"input_data\"`\n\tCSRFToken    string                 `json:\"csrf_token\"`\n}\n\n// WithQueryParamKey sets the ActionName for flowExecutionOptions.\nfunc WithQueryParamKey(key string) func(*defaultFlow) {\n\treturn func(f *defaultFlow) {\n\t\tf.queryParamKey = key\n\t}\n}\n\n// WithQueryParamValue sets the ActionName for flowExecutionOptions.\nfunc WithQueryParamValue(value string) func(*defaultFlow) {\n\treturn func(f *defaultFlow) {\n\t\tf.queryParamValue = value\n\t}\n}\n\n// WithInputData sets the InputData for flowExecutionOptions.\nfunc WithInputData(inputData InputData) func(*defaultFlow) {\n\treturn func(f *defaultFlow) {\n\t\tf.inputData = inputData\n\t}\n}\n\n// UseCompression causes the flow data to be compressed before stored to the db.\nfunc UseCompression(b bool) func(*defaultFlow) {\n\treturn func(f *defaultFlow) {\n\t\tf.useCompression = b\n\t}\n}\n\n// StateName represents the name of a state in a flow.\ntype StateName string\n\n// ActionName represents the name of an action.\ntype ActionName string\n\n// Action defines the interface for flow actions.\ntype Action interface {\n\tGetName() ActionName              // Get the action name.\n\tGetDescription() string           // Get the action description.\n\tInitialize(InitializationContext) // Initialize the action.\n\tExecute(ExecutionContext) error   // Execute the action.\n}\n\n// Actions represents a list of Action\ntype Actions []Action\n\n// HookAction defines the interface for a hook action.\ntype HookAction interface {\n\tExecute(HookExecutionContext) error\n}\n\n// hookActions represents a list of HookAction interfaces.\ntype hookActions []HookAction\n\nfunc (actions hookActions) makeUnique() hookActions {\n\tseen := make(map[HookAction]bool)\n\tvar uniqueSlice []HookAction\n\n\tfor _, action := range actions {\n\t\tif _, found := seen[action]; !found {\n\t\t\tseen[action] = true\n\t\t\tuniqueSlice = append(uniqueSlice, action)\n\t\t}\n\t}\n\n\treturn uniqueSlice\n}\n\nfunc (actions hookActions) reverse() hookActions {\n\ta := make(hookActions, len(actions))\n\tcopy(a, actions)\n\tn := reflect.ValueOf(a).Len()\n\tswap := reflect.Swapper(a)\n\tfor i, j := 0, n-1; i < j; i, j = i+1, j-1 {\n\t\tswap(i, j)\n\t}\n\treturn a\n}\n\n// stateActions maps state names to associated actions.\ntype stateActions map[StateName]Actions\n\n// stateActions maps state names to associated hook actions.\ntype stateHooks map[StateName]hookActions\n\nfunc (sh stateHooks) makeUnique() {\n\tfor stateName, actions := range sh {\n\t\tsh[stateName] = actions.makeUnique()\n\t}\n}\n\n// flowHooks maps state names to associated hook actions.\ntype flowHooks map[FlowName]hookActions\n\nfunc (fh flowHooks) makeUnique() {\n\tfor stateName, actions := range fh {\n\t\tfh[stateName] = actions.makeUnique()\n\t}\n}\n\n// stateExists checks if a state exists in the flow.\nfunc (st stateActions) stateExists(stateName StateName) bool {\n\t_, ok := st[stateName]\n\treturn ok\n}\n\n// SubFlows represents a list of SubFlow interfaces.\ntype SubFlows []subFlow\n\n// stateExists checks if the given state exists in a sub-flow of the current flow.\nfunc (sfs SubFlows) stateExists(state StateName) bool {\n\tfor _, sf := range sfs {\n\t\tif sf.getFlow().stateExists(state) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (sfs SubFlows) getSubFlowFromStateName(state StateName) subFlow {\n\tfor _, sf := range sfs {\n\t\tif sf.getFlow().stateExists(state) {\n\t\t\treturn sf\n\t\t}\n\t}\n\treturn nil\n}\n\n// flowBase represents the base of the flow interfaces.\ntype flowBase interface {\n\tgetName() FlowName\n\tgetSubFlows() SubFlows\n\tgetFlow() stateActions\n\tgetBeforeStateHooks() stateHooks\n\tgetAfterStateHooks() stateHooks\n\tgetAfterFlowHooks() hookActions\n}\n\n// Flow represents a flow.\ntype Flow interface {\n\t// Execute executes the flow using the provided FlowDB and options.\n\t// It returns the result of the flow execution and an error if any.\n\tExecute(db FlowDB, opts ...func(*defaultFlow)) (FlowResult, error)\n\t// ResultFromError converts an error into a FlowResult.\n\tResultFromError(err error) FlowResult\n\t// Set sets a value with the given key in the flow context.\n\tSet(string, interface{})\n\t// setDefaults sets the default values for the flow.\n\tsetDefaults()\n\t// getState retrieves the details of a specific state in the flow.\n\tgetState(stateName StateName) (stateDetail, error)\n\t// Embed the flowBase interface.\n\tflowBase\n}\n\n// subFlow represents a sub-flow.\ntype subFlow interface {\n\tflowBase\n}\n\ntype contextValues map[string]interface{}\n\ntype defaultFlowBase struct {\n\tname                  FlowName\n\tflow                  stateActions // StateName to Actions mapping.\n\tsubFlows              SubFlows     // The sub-flows of the current flow.\n\tbeforeStateHooks      stateHooks   // StateName to hookActions mapping.\n\tafterStateHooks       stateHooks   // StateName to hookActions mapping.\n\tbeforeEachActionHooks hookActions  // List of hookActions that run before each action.\n\tafterEachActionHooks  hookActions  // List of hookActions that run after each action.\n\tafterFlowHooks        flowHooks\n}\n\n// defaultFlow defines a flow structure with states, actions, and settings.\ntype defaultFlow struct {\n\tstateDetails      stateDetails  // Maps state names to flow details.\n\tinitialStateNames []StateName   // A list of next states in case a sub-flow should be invoked initially.\n\terrorStateName    StateName     // State representing errors.\n\tttl               time.Duration // Time-to-live for the flow.\n\tdebug             bool          // Enables debug mode.\n\tqueryParam        queryParam    // TODO\n\tcontextValues     contextValues // Values to be used within the flow context.\n\tinputData         InputData\n\tuseCompression    bool\n\tqueryParamKey     string\n\tqueryParamValue   string\n\n\t*defaultFlowBase\n}\n\nfunc (f *defaultFlow) Set(name string, value interface{}) {\n\tf.contextValues[name] = value\n}\n\n// getActionsForState returns state details for the specified state.\nfunc (f *defaultFlow) getState(stateName StateName) (stateDetail, error) {\n\tif state, ok := f.stateDetails[stateName]; ok {\n\t\treturn state, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"unknown state: %s\", stateName)\n}\n\n// getName returns the flow name.\nfunc (f *defaultFlowBase) getName() FlowName {\n\treturn f.name\n}\n\n// getSubFlows returns the sub-flows of the current flow.\nfunc (f *defaultFlowBase) getSubFlows() SubFlows {\n\treturn f.subFlows\n}\n\n// getFlow returns the state to action mapping of the current flow.\nfunc (f *defaultFlowBase) getFlow() stateActions {\n\treturn f.flow\n}\n\nfunc (f *defaultFlowBase) getBeforeStateHooks() stateHooks {\n\treturn f.beforeStateHooks\n}\n\nfunc (f *defaultFlowBase) getAfterStateHooks() stateHooks {\n\treturn f.afterStateHooks\n}\n\nfunc (f *defaultFlowBase) getAfterFlowHooks() hookActions {\n\treturn f.afterFlowHooks[f.name]\n}\n\n// setDefaults sets default values for defaultFlow settings.\nfunc (f *defaultFlow) setDefaults() {\n\tif f.ttl.Seconds() == 0 {\n\t\tf.ttl = time.Minute * 60\n\t}\n\n\tif len(f.queryParamKey) == 0 {\n\t\tf.queryParamKey = \"flowpilot_action\"\n\t}\n}\n\n// Execute handles the execution of actions for a defaultFlow.\nfunc (f *defaultFlow) Execute(db FlowDB, opts ...func(flow *defaultFlow)) (FlowResult, error) {\n\tfor _, option := range opts {\n\t\toption(f)\n\t}\n\n\t// Set default values for flow settings.\n\tf.setDefaults()\n\n\t// If the action is empty, create a new flow.\n\tif len(f.queryParamValue) == 0 {\n\t\treturn createAndInitializeFlow(db, *f)\n\t}\n\n\t// Otherwise, update an existing flow...\n\tq, err := newQueryParam(f.queryParamKey, f.queryParamValue)\n\tif err != nil {\n\t\treturn newFlowResultFromError(f.errorStateName, ErrorTechnical.Wrap(err), f.debug), nil\n\t}\n\n\tf.queryParam = q\n\n\treturn executeFlowAction(db, *f)\n}\n\n// ResultFromError returns an error response for the defaultFlow.\nfunc (f *defaultFlow) ResultFromError(err error) FlowResult {\n\tflowError := ErrorTechnical\n\n\tif e, ok := err.(FlowError); ok {\n\t\tflowError = e\n\t} else {\n\t\tflowError = flowError.Wrap(err)\n\t}\n\n\treturn newFlowResultFromError(f.errorStateName, flowError, f.debug)\n}\n"
  },
  {
    "path": "backend/flowpilot/input.go",
    "content": "package flowpilot\n\nimport (\n\t\"regexp\"\n)\n\n// inputType represents the type of the input field.\ntype inputType string\n\n// Input types enumeration.\nconst (\n\tinputTypeString   inputType = \"string\"\n\tinputTypeBoolean  inputType = \"boolean\"\n\tinputTypeEmail    inputType = \"email\"\n\tinputTypeNumber   inputType = \"number\"\n\tinputTypePassword inputType = \"password\"\n\tinputTypeJSON     inputType = \"json\"\n)\n\n// Input defines the interface for input fields.\ntype Input interface {\n\tMinLength(minLength int) Input\n\tMaxLength(maxLength int) Input\n\tRequired(b bool) Input\n\tHidden(b bool) Input\n\tPreserve(b bool) Input\n\tAllowedValue(name string, value interface{}) Input\n\tTrimSpace(b bool) Input\n\tLowerCase(b bool) Input\n\n\tsetValue(value interface{}) Input\n\tsetError(inputError InputError)\n\tgetError() InputError\n\tgetName() string\n\tshouldPreserve() bool\n\tshouldTrimSpace() bool\n\tshouldConvertToLowerCase() bool\n\tvalidate(inputData readOnlyActionInput) bool\n\ttoResponseInput() *ResponseInput\n}\n\n// defaultExtraInputOptions holds additional input field options.\ntype defaultExtraInputOptions struct {\n\tpreserveValue bool\n\ttrimSpace     bool\n\tlowerCase     bool\n}\n\n// defaultInput represents an input field with its options.\ntype defaultInput struct {\n\tname          string\n\tdataType      inputType\n\tvalue         interface{}\n\tminLength     *int\n\tmaxLength     *int\n\trequired      *bool\n\thidden        *bool\n\terror         InputError\n\tallowedValues allowedValues\n\n\tdefaultExtraInputOptions\n}\n\nfunc (i *defaultInput) AllowedValue(name string, value interface{}) Input {\n\ti.allowedValues.add(&defaultAllowedValue{\n\t\tvalue: value,\n\t\ttext:  name,\n\t})\n\treturn i\n}\n\n// newInput creates a new input instance with provided parameters.\nfunc newInput(name string, inputType inputType) Input {\n\textraOptions := defaultExtraInputOptions{\n\t\tpreserveValue: false,\n\t\ttrimSpace:     false,\n\t\tlowerCase:     false,\n\t}\n\n\treturn &defaultInput{\n\t\tname:                     name,\n\t\tdataType:                 inputType,\n\t\tdefaultExtraInputOptions: extraOptions,\n\t\tallowedValues:            &defaultAllowedValues{},\n\t}\n}\n\n// StringInput creates a new input field of string type.\nfunc StringInput(name string) Input {\n\treturn newInput(name, inputTypeString)\n}\n\n// EmailInput creates a new input field of email type.\nfunc EmailInput(name string) Input {\n\treturn newInput(name, inputTypeEmail)\n}\n\n// NumberInput creates a new input field of number type.\nfunc NumberInput(name string) Input {\n\treturn newInput(name, inputTypeNumber)\n}\n\n// BooleanInput creates a new input field of boolean type.\nfunc BooleanInput(name string) Input {\n\treturn newInput(name, inputTypeBoolean)\n}\n\n// PasswordInput creates a new input field of password type.\nfunc PasswordInput(name string) Input {\n\treturn newInput(name, inputTypePassword)\n}\n\n// JSONInput creates a new input field of JSON type.\nfunc JSONInput(name string) Input {\n\treturn newInput(name, inputTypeJSON)\n}\n\n// MinLength sets the minimum length for the input field.\nfunc (i *defaultInput) MinLength(minLength int) Input {\n\ti.minLength = &minLength\n\treturn i\n}\n\n// MaxLength sets the maximum length for the input field.\nfunc (i *defaultInput) MaxLength(maxLength int) Input {\n\ti.maxLength = &maxLength\n\treturn i\n}\n\n// Required sets whether the input field is required.\nfunc (i *defaultInput) Required(b bool) Input {\n\ti.required = &b\n\treturn i\n}\n\n// Hidden sets whether the input field is hidden.\nfunc (i *defaultInput) Hidden(b bool) Input {\n\ti.hidden = &b\n\treturn i\n}\n\n// Preserve sets whether the input field value should be preserved, so that the value is included in the response\n// instead of being blanked out.\nfunc (i *defaultInput) Preserve(b bool) Input {\n\ti.preserveValue = b\n\treturn i\n}\n\n// TrimSpace sets whether the leading and trailing whitespaces should be trimmed.\nfunc (i *defaultInput) TrimSpace(b bool) Input {\n\ti.trimSpace = b\n\treturn i\n}\n\n// LowerCase sets whether the value should be converted to lower case.\nfunc (i *defaultInput) LowerCase(b bool) Input {\n\ti.lowerCase = b\n\treturn i\n}\n\n// setValue sets the value for the input field for the current response.\nfunc (i *defaultInput) setValue(value interface{}) Input {\n\ti.value = &value\n\treturn i\n}\n\n// getName returns the name of the input field.\nfunc (i *defaultInput) getName() string {\n\treturn i.name\n}\n\n// setError sets an error to the given input field.\nfunc (i *defaultInput) setError(inputError InputError) {\n\ti.error = inputError\n}\n\n// getError returns the input error.\nfunc (i *defaultInput) getError() InputError {\n\treturn i.error\n}\n\n// shouldPreserve indicates the value should be preserved.\nfunc (i *defaultInput) shouldPreserve() bool {\n\treturn i.preserveValue\n}\n\n// shouldTrimSpace indicates the value should be trimmed.\nfunc (i *defaultInput) shouldTrimSpace() bool {\n\treturn i.trimSpace\n}\n\n// shouldConvertToLowerCase indicates the value should be converted to lower case.\nfunc (i *defaultInput) shouldConvertToLowerCase() bool {\n\treturn i.lowerCase\n}\n\n// validate performs validation on the input field.\nfunc (i *defaultInput) validate(inputData readOnlyActionInput) bool {\n\t// TODO: Replace with more structured validation logic.\n\n\tvar inputValue *string\n\n\tif v := inputData.Get(i.name); v.Exists() {\n\t\tinputValue = &v.Str\n\t}\n\n\tif i.dataType == inputTypeJSON {\n\t\t// skip further validation\n\t\treturn true\n\t}\n\n\tif i.dataType == inputTypeBoolean {\n\t\treturn true\n\t}\n\n\tisRequired := i.required != nil && *i.required\n\thasEmptyOrNilValue := inputValue == nil || len(*inputValue) <= 0\n\n\tif isRequired && hasEmptyOrNilValue {\n\t\ti.error = ErrorValueMissing\n\t\treturn false\n\t}\n\n\tif !hasEmptyOrNilValue && i.minLength != nil {\n\t\tif len(*inputValue) < *i.minLength {\n\t\t\ti.error = ErrorValueTooShort\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif !hasEmptyOrNilValue && i.maxLength != nil {\n\t\tif len(*inputValue) > *i.maxLength {\n\t\t\ti.error = ErrorValueTooLong\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif i.dataType == inputTypeEmail && (isRequired || (!isRequired && !hasEmptyOrNilValue)) {\n\t\tpattern := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$`)\n\t\tif matched := pattern.MatchString(*inputValue); !matched {\n\t\t\ti.error = ErrorValueInvalid\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif i.dataType == inputTypeString && inputValue != nil {\n\t\tif i.allowedValues.isAllowed(*inputValue) {\n\t\t\treturn true\n\t\t}\n\n\t\ti.error = createMustBeOneOfError(i.allowedValues.getValues())\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// toResponseInput converts the defaultInput to a ResponseInput for public exposure.\nfunc (i *defaultInput) toResponseInput() *ResponseInput {\n\tvar e *ResponseError\n\tvar av *ResponseAllowedValues\n\n\tif i.error != nil {\n\t\te = i.error.toResponseError(true)\n\t}\n\n\tif i.allowedValues != nil && i.allowedValues.hasAny() {\n\t\tav = i.allowedValues.toResponseAllowedValues()\n\t}\n\n\treturn &ResponseInput{\n\t\tName:          i.name,\n\t\tType:          i.dataType,\n\t\tValue:         i.value,\n\t\tMinLength:     i.minLength,\n\t\tMaxLength:     i.maxLength,\n\t\tRequired:      i.required,\n\t\tHidden:        i.hidden,\n\t\tError:         e,\n\t\tAllowedValues: av,\n\t}\n}\n"
  },
  {
    "path": "backend/flowpilot/input_allowed_value.go",
    "content": "package flowpilot\n\ntype allowedValue interface {\n\ttoResponseAllowedValue() *ResponseAllowedValue\n\tgetValue() interface{}\n}\n\ntype defaultAllowedValue struct {\n\ttext  string\n\tvalue interface{}\n}\n\nfunc (av *defaultAllowedValue) getValue() interface{} {\n\treturn av.value\n}\n\n// toResponseAllowedValue converts the allowedValue to a ResponseAllowedValue for public exposure.\nfunc (av *defaultAllowedValue) toResponseAllowedValue() *ResponseAllowedValue {\n\treturn &ResponseAllowedValue{\n\t\tText:  av.text,\n\t\tValue: av.value,\n\t}\n}\n\ntype allowedValues interface {\n\tisAllowed(value string) bool\n\tadd(allowedValue)\n\ttoResponseAllowedValues() *ResponseAllowedValues\n\thasAny() bool\n\tgetValues() []string\n}\n\ntype defaultAllowedValues []allowedValue\n\nfunc (av *defaultAllowedValues) isAllowed(value string) bool {\n\tif len(*av) == 0 {\n\t\treturn true\n\t}\n\n\tfor _, v := range *av {\n\t\tif v.getValue().(string) == value {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (av *defaultAllowedValues) add(value allowedValue) {\n\t*av = append(*av, value)\n}\n\nfunc (av *defaultAllowedValues) hasAny() bool {\n\treturn len(*av) > 0\n}\n\nfunc (av *defaultAllowedValues) getValues() []string {\n\tvalues := make([]string, len(*av))\n\tfor i, v := range *av {\n\t\tvalues[i] = v.getValue().(string)\n\t}\n\treturn values\n}\n\nfunc (av *defaultAllowedValues) toResponseAllowedValues() *ResponseAllowedValues {\n\tvalues := make(ResponseAllowedValues, len(*av))\n\tfor i, v := range *av {\n\t\tvalues[i] = v.toResponseAllowedValue()\n\t}\n\treturn &values\n}\n"
  },
  {
    "path": "backend/flowpilot/input_schema.go",
    "content": "package flowpilot\n\nimport (\n\t\"github.com/tidwall/gjson\"\n\t\"strings\"\n)\n\n// initializationInputSchema represents an interface for managing input data schemas.\ntype initializationInputSchema interface {\n\tAddInputs(inputList ...Input)\n}\n\n// executionInputSchema represents an interface for managing method execution schemas.\ntype executionInputSchema interface {\n\tGet(path string) gjson.Result\n\tSet(path string, value interface{}) error\n\tSetError(inputName string, inputError InputError)\n\n\tgetInput(name string) Input\n\tgetOutputData() readOnlyActionInput\n\tvalidateInputData() bool\n\tforInitializationContext() initializationInputSchema\n\ttoResponseInputs() ResponseInputs\n}\n\n// inputs represents a collection of Input instances.\ntype inputs []Input\n\nfunc (il *inputs) exists(input Input) bool {\n\tfor _, existingInput := range *il {\n\t\tif existingInput.getName() == input.getName() {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// ResponseInputs represents a collection of ResponseInput instances.\ntype ResponseInputs map[string]*ResponseInput\n\n// defaultSchema implements the initializationInputSchema interface and holds a collection of input fields.\ntype defaultSchema struct {\n\tinputs\n\tinputData  actionInput\n\toutputData actionInput\n}\n\n// newSchemaWithInputData creates a new executionInputSchema with input data.\nfunc newSchemaWithInputData(inputData actionInput) executionInputSchema {\n\toutputData := newActionInput()\n\treturn &defaultSchema{\n\t\tinputData:  inputData,\n\t\toutputData: outputData,\n\t}\n}\n\n// newSchema creates a new executionInputSchema with no input data.\nfunc newSchema() executionInputSchema {\n\tinputData := newActionInput()\n\treturn newSchemaWithInputData(inputData)\n}\n\n// toInitializationSchema converts executionInputSchema to initializationInputSchema.\nfunc (s *defaultSchema) forInitializationContext() initializationInputSchema {\n\treturn s\n}\n\n// Get retrieves a value at the specified path in the input data.\nfunc (s *defaultSchema) Get(path string) gjson.Result {\n\treturn s.inputData.Get(path)\n}\n\n// Set updates the JSON data at the specified path with the provided value.\nfunc (s *defaultSchema) Set(path string, value interface{}) error {\n\treturn s.outputData.Set(path, value)\n}\n\n// AddInputs adds input fields to the defaultSchema and returns the updated inputSchema.\nfunc (s *defaultSchema) AddInputs(inputList ...Input) {\n\tfor _, input := range inputList {\n\t\tif !s.inputs.exists(input) {\n\t\t\ts.inputs = append(s.inputs, input)\n\t\t} else {\n\t\t\tfor i, existingInput := range s.inputs {\n\t\t\t\tif existingInput.getName() == input.getName() {\n\t\t\t\t\tinput.setError(existingInput.getError())\n\t\t\t\t\ts.inputs[i] = input\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// getInput retrieves an input field from the inputSchema based on its name.\nfunc (s *defaultSchema) getInput(name string) Input {\n\tfor _, input := range s.inputs {\n\t\tif input.getName() == name {\n\t\t\treturn input\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// SetError sets an error for an input field in the inputSchema.\nfunc (s *defaultSchema) SetError(inputName string, inputError InputError) {\n\tif input := s.getInput(inputName); input != nil {\n\t\tinput.setError(inputError)\n\t}\n}\n\n// validateInputData validates the input data based on the input definitions in the inputSchema.\nfunc (s *defaultSchema) validateInputData() bool {\n\tfor _, input := range s.inputs {\n\t\tname := input.getName()\n\n\t\tif input.shouldTrimSpace() {\n\t\t\tv := strings.TrimSpace(s.inputData.Get(name).String())\n\t\t\t_ = s.inputData.Set(name, v)\n\t\t}\n\n\t\tif input.shouldConvertToLowerCase() {\n\t\t\tv := strings.ToLower(s.inputData.Get(name).String())\n\t\t\t_ = s.inputData.Set(name, v)\n\t\t}\n\t}\n\n\tvalid := true\n\n\tfor _, input := range s.inputs {\n\t\tif !input.validate(s.inputData) && valid {\n\t\t\tvalid = false\n\t\t}\n\t}\n\n\treturn valid\n}\n\n// getOutputData returns the output data from the inputSchema.\nfunc (s *defaultSchema) getOutputData() readOnlyActionInput {\n\treturn s.outputData\n}\n\n// toResponseInputs converts defaultSchema to ResponseInputs for public exposure.\nfunc (s *defaultSchema) toResponseInputs() ResponseInputs {\n\tvar publicSchema = make(ResponseInputs)\n\n\tfor _, input := range s.inputs {\n\t\tif s.outputData.Get(input.getName()).Exists() {\n\t\t\tinput.setValue(s.outputData.Get(input.getName()).Value())\n\t\t} else if input.shouldPreserve() {\n\t\t\tinput.setValue(s.inputData.Get(input.getName()).Value())\n\t\t}\n\n\t\tpublicSchema[input.getName()] = input.toResponseInput()\n\t}\n\n\treturn publicSchema\n}\n"
  },
  {
    "path": "backend/flowpilot/jsonmanager/manager.go",
    "content": "package jsonmanager\n\nimport (\n\t\"errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n)\n\n// ReadJSONManager is the interface that allows read operations.\ntype ReadJSONManager interface {\n\tGet(path string) gjson.Result // Get retrieves the value at the specified path in the JSON data.\n\tString() string               // String returns the JSON data as a string.\n\tUnmarshal() interface{}       // Unmarshal parses the JSON data and returns it as an interface{}.\n}\n\n// JSONManager is the interface that defines methods for reading, writing, and deleting JSON data.\ntype JSONManager interface {\n\tReadJSONManager\n\tSet(path string, value interface{}) error // Set updates the JSON data at the specified path with the provided value.\n\tDelete(path string) error                 // Delete removes a value from the JSON data at the specified path.\n}\n\n// ReadOnlyJSONManager is the interface that allows only read operations.\ntype ReadOnlyJSONManager interface {\n\tReadJSONManager\n}\n\n// DefaultJSONManager is the default implementation of the JSONManager interface.\ntype DefaultJSONManager struct {\n\tdata string // The JSON data stored as a string.\n}\n\n// NewJSONManager creates a new instance of DefaultJSONManager with empty JSON data.\nfunc NewJSONManager() JSONManager {\n\treturn &DefaultJSONManager{data: \"{}\"}\n}\n\n// NewJSONManagerFromString creates a new instance of DefaultJSONManager with the given JSON data.\n// It checks if the provided data is valid JSON before creating the instance.\nfunc NewJSONManagerFromString(data string) (JSONManager, error) {\n\tif !gjson.Valid(data) {\n\t\treturn nil, errors.New(\"invalid json\")\n\t}\n\treturn &DefaultJSONManager{data: data}, nil\n}\n\n// Get retrieves the value at the specified path in the JSON data.\nfunc (jm *DefaultJSONManager) Get(path string) gjson.Result {\n\treturn gjson.Get(jm.data, path)\n}\n\n// Set updates the JSON data at the specified path with the provided value.\nfunc (jm *DefaultJSONManager) Set(path string, value interface{}) error {\n\tnewData, err := sjson.Set(jm.data, path, value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tjm.data = newData\n\treturn nil\n}\n\n// Delete removes a value from the JSON data at the specified path.\nfunc (jm *DefaultJSONManager) Delete(path string) error {\n\tnewData, err := sjson.Delete(jm.data, string(path))\n\tif err != nil {\n\t\treturn err\n\t}\n\tjm.data = newData\n\treturn nil\n}\n\n// String returns the JSON data as a string.\nfunc (jm *DefaultJSONManager) String() string {\n\treturn jm.data\n}\n\n// Unmarshal parses the JSON data and returns it as an interface{}.\nfunc (jm *DefaultJSONManager) Unmarshal() interface{} {\n\tm, ok := gjson.Parse(jm.data).Value().(interface{})\n\tif !ok {\n\t\treturn nil\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "backend/flowpilot/link.go",
    "content": "package flowpilot\n\n// LinkCategory represents the category of the link.\ntype LinkCategory string\n\n// LinkTarget represents the html target attribute.\ntype LinkTarget string\n\n// Link targets enumeration.\nconst (\n\tLinkTargetSelf   LinkTarget = \"_self\"\n\tLinkTargetBlank  LinkTarget = \"_blank\"\n\tLinkTargetParent LinkTarget = \"_parent\"\n\tLinkTargetTop    LinkTarget = \"_top\"\n)\n\n// Link defines the interface for links.\ntype Link interface {\n\tTarget(LinkTarget) Link\n\n\ttoResponseLink() ResponseLink\n}\n\n// defaultLink represents a link with its options.\ntype defaultLink struct {\n\tname     string\n\thref     string\n\tcategory LinkCategory\n\ttarget   LinkTarget\n}\n\n// Target sets the target attribute of the link.\nfunc (l *defaultLink) Target(target LinkTarget) Link {\n\tl.target = target\n\treturn l\n}\n\nfunc (l *defaultLink) toResponseLink() ResponseLink {\n\treturn ResponseLink{\n\t\tName:     l.name,\n\t\tHref:     l.href,\n\t\tCategory: l.category,\n\t\tTarget:   l.target,\n\t}\n}\n\n// NewLink creates a new defaultLink instance with provided parameters.\nfunc NewLink(name string, category LinkCategory, href string) Link {\n\treturn &defaultLink{\n\t\tname:     name,\n\t\thref:     href,\n\t\tcategory: category,\n\t\ttarget:   LinkTargetSelf,\n\t}\n}\n"
  },
  {
    "path": "backend/flowpilot/payload.go",
    "content": "package flowpilot\n\nimport \"github.com/teamhanko/hanko/backend/v2/flowpilot/jsonmanager\"\n\ntype payload interface {\n\tjsonmanager.JSONManager\n}\n\n// newPayload creates a new instance of Payload with empty JSON data.\nfunc newPayload() payload {\n\treturn jsonmanager.NewJSONManager()\n}\n"
  },
  {
    "path": "backend/flowpilot/query_param.go",
    "content": "package flowpilot\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype queryParam interface {\n\tgetKey() string\n\tgetValue() string\n\tgetActionName() ActionName\n\tgetFlowID() uuid.UUID\n\tgetURLValues() url.Values\n}\n\n// parsedQueryParamValue represents a parsed action from an input string.\ntype parsedQueryParamValue struct {\n\tactionName ActionName // The actionName of the action extracted from the input string.\n\tflowID     uuid.UUID  // The UUID representing the flow ID extracted from the input string.\n}\n\n// defaultQueryParam represents a parsed action from an input string.\ntype defaultQueryParam struct {\n\tkey string\n\n\t*parsedQueryParamValue\n}\n\nfunc createQueryParamValue(actionName ActionName, flowID uuid.UUID) string {\n\treturn fmt.Sprintf(\"%s@%s\", actionName, flowID)\n}\n\n// parseValue parses an input string to extract action name and flow ID.\nfunc parseQueryParamValue(value string) (*parsedQueryParamValue, error) {\n\tif value == \"\" {\n\t\treturn nil, fmt.Errorf(\"query param value is empty\")\n\t}\n\n\t// Split the input string into action and flow ID parts using \"@\" as separator.\n\tparts := strings.SplitN(value, \"@\", 2)\n\tif len(parts) != 2 {\n\t\treturn nil, fmt.Errorf(\"invalid query param value format\")\n\t}\n\n\t// Extract action name from the first part of the split.\n\taction := parts[0]\n\tif len(action) == 0 {\n\t\treturn nil, fmt.Errorf(\"first part of the query param value is empty\")\n\t}\n\n\t// Parse the second part of the input string into a UUID representing the flow ID.\n\tflowID, err := uuid.FromString(parts[1])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse second part of the query param value: %w\", err)\n\t}\n\n\t// Return a defaultQueryParam instance with extracted action name and flow ID.\n\treturn &parsedQueryParamValue{actionName: ActionName(action), flowID: flowID}, nil\n}\n\nfunc newQueryParam(key, value string) (queryParam, error) {\n\tv, err := parseQueryParamValue(value)\n\treturn &defaultQueryParam{key: key, parsedQueryParamValue: v}, err\n}\n\nfunc (q *defaultQueryParam) getKey() string {\n\treturn q.key\n}\n\nfunc (q *defaultQueryParam) getValue() string {\n\treturn createQueryParamValue(q.getActionName(), q.getFlowID())\n}\n\nfunc (q *defaultQueryParam) getURLValues() url.Values {\n\tvalues := url.Values{}\n\tvalues.Add(q.getKey(), q.getValue())\n\treturn values\n}\n\nfunc (q *defaultQueryParam) getActionName() ActionName {\n\treturn q.parsedQueryParamValue.actionName\n}\n\nfunc (q *defaultQueryParam) getFlowID() uuid.UUID {\n\treturn q.parsedQueryParamValue.flowID\n}\n"
  },
  {
    "path": "backend/flowpilot/random.go",
    "content": "package flowpilot\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/big\"\n)\n\nconst letters = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n\nfunc init() {\n\tassertAvailablePRNG()\n}\n\nfunc assertAvailablePRNG() {\n\t// Assert that a cryptographically secure PRNG is available.\n\t// Panic otherwise.\n\tbuf := make([]byte, 1)\n\t_, err := io.ReadFull(rand.Reader, buf)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"crypto/rand is unavailable: Read() failed with %#v\", err))\n\t}\n}\n\nfunc generateRandomString(n int) (string, error) {\n\tret := make([]byte, n)\n\tfor i := 0; i < n; i++ {\n\t\tnum, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tret[i] = letters[num.Int64()]\n\t}\n\treturn string(ret), nil\n}\n"
  },
  {
    "path": "backend/flowpilot/response.go",
    "content": "package flowpilot\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// ResponseAction represents a link to an action.\ntype ResponseAction struct {\n\tHref        string         `json:\"href\"`\n\tInputs      ResponseInputs `json:\"inputs\"`\n\tName        ActionName     `json:\"action\"`\n\tDescription string         `json:\"description\"`\n}\n\n// ResponseActions is a collection of ResponseAction instances.\ntype ResponseActions map[ActionName]ResponseAction\n\n// ResponseError represents an error for public exposure.\ntype ResponseError struct {\n\tCode     string  `json:\"code\"`\n\tMessage  string  `json:\"message\"`\n\tCause    *string `json:\"cause,omitempty\"`\n\tInternal *string `json:\"-\"`\n}\n\ntype ResponseAllowedValue struct {\n\tValue interface{} `json:\"value\"`\n\tText  string      `json:\"name\"`\n}\n\ntype ResponseAllowedValues []*ResponseAllowedValue\n\n// ResponseInput represents an input field for public exposure.\ntype ResponseInput struct {\n\tName          string                 `json:\"name\"`\n\tType          inputType              `json:\"type\"`\n\tValue         interface{}            `json:\"value,omitempty\"`\n\tMinLength     *int                   `json:\"min_length,omitempty\"`\n\tMaxLength     *int                   `json:\"max_length,omitempty\"`\n\tRequired      *bool                  `json:\"required,omitempty\"`\n\tHidden        *bool                  `json:\"hidden,omitempty\"`\n\tError         *ResponseError         `json:\"error,omitempty\"`\n\tAllowedValues *ResponseAllowedValues `json:\"allowed_values,omitempty\"`\n}\n\n// ResponseLinks is a collection of Link instances.\ntype ResponseLinks []ResponseLink\n\n// ResponseLink represents a link for public exposure.\ntype ResponseLink struct {\n\tName     string       `json:\"name\"` // tos, privacy, google, apple, microsoft, login, registration ... // how can we insert custom oauth provider here\n\tHref     string       `json:\"href\"`\n\tCategory LinkCategory `json:\"category\"` // oauth, legal, other, ...\n\tTarget   LinkTarget   `json:\"target\"`   // can be used to add the target of the a-tag e.g. _blank\n}\n\n// Response represents the response of an action execution.\ntype Response struct {\n\tName      StateName       `json:\"name\"`\n\tStatus    int             `json:\"status\"`\n\tPayload   interface{}     `json:\"payload,omitempty\"`\n\tCSRFToken string          `json:\"csrf_token\"`\n\tActions   ResponseActions `json:\"actions\"`\n\tError     *ResponseError  `json:\"error,omitempty\"`\n\tLinks     ResponseLinks   `json:\"links\"`\n}\n\n// FlowResult interface defines methods for obtaining response and status.\ntype FlowResult interface {\n\tGetResponse() Response\n\tGetStatus() int\n}\n\n// defaultFlowResult implements FlowResult interface.\ntype defaultFlowResult struct {\n\tresponse Response\n}\n\n// newFlowResultFromResponse creates a FlowResult from a Response.\nfunc newFlowResultFromResponse(response Response) FlowResult {\n\treturn defaultFlowResult{response: response}\n}\n\n// newFlowResultFromError creates a FlowResult from a FlowError.\nfunc newFlowResultFromError(stateName StateName, flowError FlowError, debug bool) FlowResult {\n\te := flowError.toResponseError(debug)\n\tstatus := flowError.Status()\n\n\tresponse := Response{\n\t\tName:    stateName,\n\t\tStatus:  status,\n\t\tError:   e,\n\t\tActions: ResponseActions{},\n\t}\n\n\treturn defaultFlowResult{response: response}\n}\n\n// GetResponse returns the Response.\nfunc (r defaultFlowResult) GetResponse() Response {\n\treturn r.response\n}\n\n// GetStatus returns the HTTP status code.\nfunc (r defaultFlowResult) GetStatus() int {\n\treturn r.response.Status\n}\n\n// actionExecutionResult holds the result of a method execution.\ntype actionExecutionResult struct {\n\tactionName  ActionName\n\tinputSchema executionInputSchema\n\tisSuspended bool\n}\n\n// executionResult holds the result of an action execution.\ntype executionResult struct {\n\tnextStateName StateName\n\tflowError     FlowError\n\tlinks         []Link\n\n\t*actionExecutionResult\n}\n\n// generateResponse generates a response based on the execution result.\nfunc (er *executionResult) generateResponse(fc *defaultFlowContext) FlowResult {\n\t// Generate actions for the response.\n\tactions := er.generateActions(fc)\n\n\t// Unmarshal the generated payload for the response.\n\tp := fc.payload.Unmarshal()\n\n\t// Generate links for the response.\n\tlinks := er.generateLinks()\n\n\t// Create the response object.\n\tresp := Response{\n\t\tName:      er.nextStateName,\n\t\tStatus:    http.StatusOK,\n\t\tPayload:   p,\n\t\tActions:   actions,\n\t\tLinks:     links,\n\t\tCSRFToken: fc.flowModel.CSRFToken,\n\t}\n\n\t// Include flow error if present.\n\tif er.flowError != nil {\n\t\tstatus := er.flowError.Status()\n\t\te := er.flowError.toResponseError(fc.flow.debug)\n\n\t\tresp.Status = status\n\t\tresp.Error = e\n\t}\n\n\treturn newFlowResultFromResponse(resp)\n}\n\nfunc (er *executionResult) generateLinks() ResponseLinks {\n\tvar links ResponseLinks\n\n\tfor _, link := range er.links {\n\t\tl := link.toResponseLink()\n\t\tlinks = append(links, l)\n\t}\n\n\treturn links\n}\n\n// generateActions generates a collection of links based on the execution result.\nfunc (er *executionResult) generateActions(fc *defaultFlowContext) ResponseActions {\n\tvar actions = make(ResponseActions)\n\n\t// Get actions for the next addState.\n\tstate, _ := fc.flow.getState(er.nextStateName)\n\n\tif state != nil {\n\t\tfor _, ad := range state.getActionDetails() {\n\t\t\tactionName := ad.getAction().GetName()\n\t\t\tactionDescription := ad.getAction().GetDescription()\n\n\t\t\t// Create action HREF based on the current flow context and method name.\n\t\t\thref, _ := er.createHref(fc, actionName)\n\t\t\tinputSchema := er.getInputSchema(fc, ad)\n\n\t\t\t// (Re-)Initialize each action\n\t\t\taic := defaultActionInitializationContext{\n\t\t\t\tinputSchema:        inputSchema.forInitializationContext(),\n\t\t\t\tdefaultFlowContext: fc,\n\t\t\t}\n\n\t\t\tad.getAction().Initialize(&aic)\n\n\t\t\tif aic.isSuspended {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tinputSchemaResponse := inputSchema.toResponseInputs()\n\n\t\t\t// Create the action instance.\n\t\t\taction := ResponseAction{\n\t\t\t\tHref:        href,\n\t\t\t\tInputs:      inputSchemaResponse,\n\t\t\t\tName:        actionName,\n\t\t\t\tDescription: actionDescription,\n\t\t\t}\n\n\t\t\tactions[actionName] = action\n\t\t}\n\t}\n\n\treturn actions\n}\n\n// getInputSchema returns the inputSchema for a given method name.\nfunc (er *executionResult) getInputSchema(fc *defaultFlowContext, actionDetail actionDetail) executionInputSchema {\n\tactionName := actionDetail.getAction().GetName()\n\tif er.actionExecutionResult == nil || actionName != er.actionExecutionResult.actionName {\n\t\treturn newSchema()\n\t}\n\treturn er.actionExecutionResult.inputSchema\n}\n\n// createHref creates a link HREF based on the current flow context and method name.\nfunc (er *executionResult) createHref(fc *defaultFlowContext, actionName ActionName) (string, error) {\n\tq, err := newQueryParam(fc.flow.queryParamKey, createQueryParamValue(actionName, fc.GetFlowID()))\n\treturn fmt.Sprintf(\"/%s?%s\", fc.GetFlowName(), q.getURLValues().Encode()), err\n}\n"
  },
  {
    "path": "backend/flowpilot/stash.go",
    "content": "package flowpilot\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot/jsonmanager\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\t\"io\"\n)\n\nconst (\n\tstashKeyState           = \"state\"\n\tstashKeyPreviousState   = \"prev_state\"\n\tstashKeyScheduledStates = \"scheduled\"\n\tstashKeyData            = \"data\"\n\tstashKeyHistory         = \"hist\"\n\tstashKeyRevertible      = \"revertible\"\n\tstashKeySticky          = \"sticky\"\n)\n\ntype stash interface {\n\tpushState(bool) error\n\tpushErrorState(StateName) error\n\trevertState() error\n\tisRevertible() bool\n\tgetStateName() StateName\n\tgetPreviousStateName() StateName\n\tgetNextStateName() StateName\n\taddScheduledStateNames(...StateName)\n\tgetScheduledStateNames() []StateName\n\tuseCompression(bool)\n\tstateVisited(name StateName) bool\n\n\tjsonmanager.JSONManager\n}\n\ntype defaultStash struct {\n\tjm                  jsonmanager.JSONManager\n\tdata                jsonmanager.JSONManager\n\tscheduledStateNames []StateName\n\tcompressionEnabled  bool\n}\n\n// newStashFromJSONManager creates a new instance of stash with a given JSONManager.\nfunc newStashFromJSONManager(jm jsonmanager.JSONManager) stash {\n\tdata, _ := jsonmanager.NewJSONManagerFromString(jm.Get(stashKeyData).String())\n\treturn &defaultStash{\n\t\tjm:                  jm,\n\t\tdata:                data,\n\t\tscheduledStateNames: make([]StateName, 0),\n\t\tcompressionEnabled:  false,\n\t}\n}\n\n// newStash creates a new instance of Stash with empty JSON data.\nfunc newStash(nextStates ...StateName) (stash, error) {\n\tjm := jsonmanager.NewJSONManager()\n\n\tif len(nextStates) == 0 {\n\t\treturn nil, errors.New(\"can't create a new stash without a state name\")\n\t}\n\n\tif err := jm.Set(stashKeyState, nextStates[0]); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := jm.Set(stashKeyScheduledStates, reverseStateNames(nextStates[1:])); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := jm.Set(stashKeyData, \"{}\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newStashFromJSONManager(jm), nil\n}\n\n// newStashFromString creates a new instance of Stash with the given JSON data.\nfunc newStashFromString(data string) (stash, error) {\n\tvar err error\n\n\tif len(data) > 0 && !startsWithCurlyBrace(data) {\n\t\tif data, err = decodeData(data); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"faiiled to decode stash data: %w\", err)\n\t\t}\n\t}\n\n\tjm, err := jsonmanager.NewJSONManagerFromString(data)\n\treturn newStashFromJSONManager(jm), err\n}\n\nfunc reverseStateNames(slice []StateName) []StateName {\n\treversed := make([]StateName, len(slice))\n\tfor i, v := range slice {\n\t\treversed[len(slice)-1-i] = v\n\t}\n\treturn reversed\n}\n\nfunc startsWithCurlyBrace(s string) bool {\n\t// Check if the string is not empty\n\tif len(s) == 0 {\n\t\treturn false\n\t}\n\t// Check if the first character is '{'\n\treturn s[0] == '{'\n}\n\nfunc encodeData(jsonData string) (string, error) {\n\tvar buf bytes.Buffer\n\tgw := gzip.NewWriter(&buf)\n\tif _, err := gw.Write([]byte(jsonData)); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif err := gw.Close(); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tgzippedData := buf.Bytes()\n\tbase64GzippedData := base64.StdEncoding.EncodeToString(gzippedData)\n\treturn base64GzippedData, nil\n}\n\nfunc decodeData(base64GzippedData string) (string, error) {\n\tgzippedData, err := base64.StdEncoding.DecodeString(base64GzippedData)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tbuf := bytes.NewBuffer(gzippedData)\n\tgr, err := gzip.NewReader(buf)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tdefer gr.Close()\n\n\tdecompressedData, err := io.ReadAll(gr)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn string(decompressedData), nil\n}\n\n// Get retrieves the value at the specified path in the JSON data.\nfunc (h *defaultStash) Get(path string) gjson.Result {\n\treturn h.data.Get(path)\n}\n\n// Set updates the JSON data at the specified path with the provided value.\nfunc (h *defaultStash) Set(path string, value interface{}) error {\n\treturn h.data.Set(path, value)\n}\n\n// Delete removes a value from the JSON data at the specified path.\nfunc (h *defaultStash) Delete(path string) error {\n\treturn h.data.Delete(path)\n}\n\n// String returns the JSON data as a string.\nfunc (h *defaultStash) String() string {\n\tif h.compressionEnabled {\n\t\ts, _ := encodeData(h.jm.String())\n\t\treturn s\n\t}\n\treturn h.jm.String()\n}\n\n// Unmarshal parses the JSON data and returns it as an interface{}.\nfunc (h *defaultStash) Unmarshal() interface{} {\n\treturn h.jm.Unmarshal()\n}\n\nfunc (h *defaultStash) pushState(revertible bool) error {\n\treturn h.push(h.data.String(), revertible, h.getStateName() != h.getNextStateName())\n}\n\nfunc (h *defaultStash) pushErrorState(nextState StateName) error {\n\treturn h.push(h.jm.Get(stashKeyData).String(), h.isRevertible(), false, nextState)\n}\n\nfunc (h *defaultStash) push(newData string, revertible, writeHistory bool, nextStates ...StateName) error {\n\tvar err error\n\n\tdata := h.jm.Get(stashKeyData)\n\tscheduledStates := h.jm.Get(stashKeyScheduledStates)\n\tscheduledStatesArr := scheduledStates.Array()\n\tstateStr := h.jm.Get(stashKeyState).String()\n\tprevStateStr := h.jm.Get(stashKeyPreviousState).String()\n\n\tscheduledStatesUpdated := make([]StateName, len(scheduledStatesArr))\n\tmaxIndex := len(scheduledStatesUpdated) - 1\n\tfor index := range scheduledStatesUpdated {\n\t\tscheduledStatesUpdated[maxIndex-index] = StateName(scheduledStatesArr[index].String())\n\t}\n\n\tscheduledStatesUpdated = append(nextStates, append(h.scheduledStateNames, scheduledStatesUpdated...)...)\n\tif len(scheduledStatesUpdated) == 0 {\n\t\treturn errors.New(\"no state left to be used as the next state\")\n\t}\n\n\tnextStateName := scheduledStatesUpdated[0]\n\tscheduledStatesUpdated = reverseStateNames(scheduledStatesUpdated[1:])\n\n\tif writeHistory {\n\t\thistItem := \"{}\"\n\t\tfor key, value := range map[string]interface{}{\n\t\t\tstashKeyState:           stateStr,\n\t\t\tstashKeyPreviousState:   prevStateStr,\n\t\t\tstashKeyData:            data.Value(),\n\t\t\tstashKeyRevertible:      revertible,\n\t\t\tstashKeyScheduledStates: scheduledStates.Value(),\n\t\t} {\n\t\t\tif histItem, err = sjson.Set(histItem, key, value); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tstashKeyNewHistItem := fmt.Sprintf(\"%s.-1\", stashKeyHistory)\n\t\tif err = h.jm.Set(stashKeyNewHistItem, gjson.Parse(histItem).Value()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor key, value := range map[string]interface{}{\n\t\tstashKeyState:           nextStateName,\n\t\tstashKeyPreviousState:   stateStr,\n\t\tstashKeyData:            gjson.Parse(newData).Value(),\n\t\tstashKeyScheduledStates: scheduledStatesUpdated,\n\t} {\n\t\tif err = h.jm.Set(key, value); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (h *defaultStash) stateVisited(name StateName) bool {\n\tvisited := false\n\th.jm.Get(stashKeyHistory).ForEach(func(key, value gjson.Result) bool {\n\t\tif StateName(value.Get(stashKeyState).String()) == name {\n\t\t\tvisited = true\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t})\n\treturn visited\n}\n\nfunc (h *defaultStash) revertState() error {\n\tvar err error\n\n\tlastHistItemIndex := h.jm.Get(fmt.Sprintf(\"%s.#\", stashKeyHistory)).Int() - 1\n\tlastHistItem := h.jm.Get(fmt.Sprintf(\"%s.%d\", stashKeyHistory, lastHistItemIndex))\n\n\tif !lastHistItem.Exists() {\n\t\treturn errors.New(\"no state to revert to\")\n\t}\n\n\tif !lastHistItem.Get(stashKeyRevertible).Bool() {\n\t\treturn errors.New(\"state is not revertible\")\n\t}\n\n\tdataUpdated := lastHistItem.Get(stashKeyData)\n\th.data.Get(stashKeySticky).ForEach(func(key, value gjson.Result) bool {\n\t\tpath := fmt.Sprintf(\"%s.%s\", stashKeySticky, key.String())\n\t\tupdated, _ := sjson.Set(dataUpdated.String(), path, value.Value())\n\t\tdataUpdated = gjson.Parse(updated)\n\t\treturn true\n\t})\n\n\tif err = h.jm.Delete(fmt.Sprintf(\"%s.-1\", stashKeyHistory)); err != nil {\n\t\treturn err\n\t}\n\n\tfor key, value := range map[string]interface{}{\n\t\tstashKeyScheduledStates: lastHistItem.Get(stashKeyScheduledStates).Value(),\n\t\tstashKeyState:           lastHistItem.Get(stashKeyState).Value(),\n\t\tstashKeyPreviousState:   lastHistItem.Get(stashKeyPreviousState).Value(),\n\t\tstashKeyData:            dataUpdated.Value(),\n\t} {\n\t\tif err = h.jm.Set(key, value); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (h *defaultStash) getStateName() StateName {\n\treturn StateName(h.jm.Get(stashKeyState).String())\n}\n\nfunc (h *defaultStash) getPreviousStateName() StateName {\n\treturn StateName(h.jm.Get(stashKeyPreviousState).String())\n}\n\nfunc (h *defaultStash) addScheduledStateNames(names ...StateName) {\n\th.scheduledStateNames = append(h.scheduledStateNames, names...)\n}\nfunc (h *defaultStash) getScheduledStateNames() []StateName {\n\tvalues := h.jm.Get(stashKeyScheduledStates).Array()\n\tresult := make([]StateName, len(values))\n\tfor i, value := range values {\n\t\tresult[i] = StateName(value.String())\n\t}\n\treturn result\n}\n\nfunc (h *defaultStash) getNextStateName() StateName {\n\tif len(h.scheduledStateNames) > 0 {\n\t\treturn h.scheduledStateNames[0]\n\t}\n\n\tlastScheduledIndex := h.jm.Get(fmt.Sprintf(\"%s.#\", stashKeyScheduledStates)).Int() - 1\n\treturn StateName(h.jm.Get(fmt.Sprintf(\"%s.%d\", stashKeyScheduledStates, lastScheduledIndex)).String())\n}\n\nfunc (h *defaultStash) isRevertible() bool {\n\tlastHistItemIndex := h.jm.Get(fmt.Sprintf(\"%s.#\", stashKeyHistory)).Int() - 1\n\treturn h.jm.Get(fmt.Sprintf(\"%s.%d.%s\", stashKeyHistory, lastHistItemIndex, stashKeyRevertible)).Bool()\n}\n\nfunc (h *defaultStash) useCompression(b bool) {\n\th.compressionEnabled = b\n}\n"
  },
  {
    "path": "backend/flowpilot/state_action.go",
    "content": "package flowpilot\n\ntype actionDetail interface {\n\tgetAction() Action\n\tgetFlowName() FlowName\n}\n\ntype defaultActionDetail struct {\n\taction   Action\n\tflowName FlowName\n}\n\n// actions represents a list of action\ntype defaultActionDetails []actionDetail\n\nfunc (ad *defaultActionDetail) getAction() Action {\n\treturn ad.action\n}\n\nfunc (ad *defaultActionDetail) getFlowName() FlowName {\n\treturn ad.flowName\n}\n"
  },
  {
    "path": "backend/flowpilot/state_detail.go",
    "content": "package flowpilot\n\nimport \"fmt\"\n\ntype stateDetail interface {\n\tgetName() StateName\n\tgetFlow() stateActions\n\tgetFlowName() FlowName\n\tgetSubFlows() SubFlows\n\tgetActionDetails() defaultActionDetails\n\tgetActionDetail(actionName ActionName) (actionDetail, error)\n}\n\n// state represents details for a state, including the associated actions, available sub-flows and more.\ntype defaultStateDetail struct {\n\tname          StateName\n\tflowName      FlowName\n\tflow          stateActions\n\tsubFlows      SubFlows\n\tactionDetails defaultActionDetails\n}\n\nfunc (sd *defaultStateDetail) getName() StateName {\n\treturn sd.name\n}\n\nfunc (sd *defaultStateDetail) getFlow() stateActions {\n\treturn sd.flow\n}\n\nfunc (sd *defaultStateDetail) getFlowName() FlowName {\n\treturn sd.flowName\n}\n\nfunc (sd *defaultStateDetail) getSubFlows() SubFlows {\n\treturn sd.subFlows\n}\n\nfunc (sd *defaultStateDetail) getActionDetails() defaultActionDetails {\n\treturn sd.actionDetails\n}\n\n// getActionDetail returns the Action with the specified name.\nfunc (sd *defaultStateDetail) getActionDetail(actionName ActionName) (actionDetail, error) {\n\tfor _, ad := range sd.actionDetails {\n\t\tcurrentActionName := ad.getAction().GetName()\n\n\t\tif currentActionName == actionName {\n\t\t\treturn ad, nil\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"action '%s' not found\", actionName)\n}\n\n// stateDetails maps states to associated Actions, flows and sub-flows.\ntype stateDetails map[StateName]stateDetail\n"
  },
  {
    "path": "backend/go.mod",
    "content": "module github.com/teamhanko/hanko/backend/v2\n\ngo 1.26.1\n\nrequire (\n\tgithub.com/aws/aws-sdk-go-v2/config v1.32.12\n\tgithub.com/aws/aws-sdk-go-v2/service/kms v1.50.3\n\tgithub.com/beevik/etree v1.6.0\n\tgithub.com/brianvoe/gofakeit/v6 v6.28.0\n\tgithub.com/coreos/go-oidc/v3 v3.17.0\n\tgithub.com/evanphx/json-patch/v5 v5.9.11\n\tgithub.com/fatih/structs v1.1.0\n\tgithub.com/go-playground/validator/v10 v10.30.1\n\tgithub.com/go-redsync/redsync/v4 v4.16.0\n\tgithub.com/go-sql-driver/mysql v1.9.3\n\tgithub.com/go-testfixtures/testfixtures/v3 v3.19.0\n\tgithub.com/go-webauthn/webauthn v0.10.2\n\tgithub.com/gobuffalo/nulls v0.4.2\n\tgithub.com/gobuffalo/pop/v6 v6.1.1\n\tgithub.com/gobuffalo/validate/v3 v3.3.3\n\tgithub.com/gobwas/glob v0.2.3\n\tgithub.com/gofrs/uuid v4.4.0+incompatible\n\tgithub.com/gomodule/redigo v1.9.3\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/h2non/gock v1.2.0\n\tgithub.com/invopop/jsonschema v0.13.0\n\tgithub.com/jackc/pgconn v1.14.3\n\tgithub.com/kelseyhightower/envconfig v1.4.0\n\tgithub.com/knadh/koanf/parsers/json v1.0.0\n\tgithub.com/knadh/koanf/parsers/yaml v1.1.0\n\tgithub.com/knadh/koanf/providers/file v1.2.1\n\tgithub.com/knadh/koanf/v2 v2.3.4\n\tgithub.com/labstack/echo-contrib v0.17.4\n\tgithub.com/labstack/echo-jwt/v4 v4.4.0\n\tgithub.com/labstack/echo/v4 v4.15.1\n\tgithub.com/labstack/gommon v0.4.2\n\tgithub.com/lestrrat-go/jwx/v2 v2.1.6\n\tgithub.com/lib/pq v1.12.0\n\tgithub.com/mattermost/xml-roundtrip-validator v0.1.0\n\tgithub.com/mileusna/useragent v1.3.5\n\tgithub.com/mitchellh/mapstructure v1.5.0\n\tgithub.com/nicksnyder/go-i18n/v2 v2.6.1\n\tgithub.com/ory/dockertest/v3 v3.12.0\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/pquerna/otp v1.5.0\n\tgithub.com/rs/zerolog v1.34.0\n\tgithub.com/russellhaering/gosaml2 v0.11.0\n\tgithub.com/russellhaering/goxmldsig v1.6.0\n\tgithub.com/sethvargo/go-limiter v1.1.0\n\tgithub.com/sethvargo/go-redisstore v0.3.0\n\tgithub.com/spf13/cobra v1.10.2\n\tgithub.com/stretchr/testify v1.11.1\n\tgithub.com/tidwall/gjson v1.18.0\n\tgithub.com/tidwall/sjson v1.2.5\n\tgithub.com/wk8/go-ordered-map/v2 v2.1.8\n\tgolang.org/x/crypto v0.49.0\n\tgolang.org/x/oauth2 v0.36.0\n\tgolang.org/x/text v0.35.0\n\tgopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\nrequire (\n\tdario.cat/mergo v1.0.1 // indirect\n\tfilippo.io/edwards25519 v1.1.1 // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect\n\tgithub.com/Masterminds/semver/v3 v3.1.1 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect\n\tgithub.com/aws/aws-sdk-go-v2 v1.41.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect\n\tgithub.com/aws/smithy-go v1.24.2 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/bahlo/generic-list-go v0.2.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect\n\tgithub.com/buger/jsonparser v1.1.2 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.3.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/containerd/continuity v0.4.5 // indirect\n\tgithub.com/containerd/errdefs v1.0.0 // indirect\n\tgithub.com/containerd/errdefs/pkg v0.3.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect\n\tgithub.com/distribution/reference v0.6.0 // indirect\n\tgithub.com/docker/cli v29.2.0+incompatible // indirect\n\tgithub.com/docker/go-connections v0.6.0 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/fatih/color v1.13.0 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.6.0 // indirect\n\tgithub.com/gabriel-vasile/mimetype v1.4.12 // indirect\n\tgithub.com/go-jose/go-jose/v4 v4.1.3 // indirect\n\tgithub.com/go-logr/logr v1.4.2 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-playground/locales v0.14.1 // indirect\n\tgithub.com/go-playground/universal-translator v0.18.1 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/go-webauthn/x v0.1.9 // indirect\n\tgithub.com/gobuffalo/envy v1.10.2 // indirect\n\tgithub.com/gobuffalo/fizz v1.14.4 // indirect\n\tgithub.com/gobuffalo/flect v1.0.0 // indirect\n\tgithub.com/gobuffalo/github_flavored_markdown v1.1.3 // indirect\n\tgithub.com/gobuffalo/helpers v0.6.7 // indirect\n\tgithub.com/gobuffalo/plush/v4 v4.1.18 // indirect\n\tgithub.com/gobuffalo/tags/v3 v3.1.4 // indirect\n\tgithub.com/goccy/go-json v0.10.3 // indirect\n\tgithub.com/goccy/go-yaml v1.18.0 // indirect\n\tgithub.com/golang-jwt/jwt/v5 v5.3.0 // indirect\n\tgithub.com/google/go-tpm v0.9.0 // indirect\n\tgithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect\n\tgithub.com/gorilla/css v1.0.0 // indirect\n\tgithub.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect\n\tgithub.com/hashicorp/errwrap v1.1.0 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jackc/chunkreader/v2 v2.0.1 // indirect\n\tgithub.com/jackc/pgio v1.0.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgproto3/v2 v2.3.3 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect\n\tgithub.com/jackc/pgtype v1.14.4 // indirect\n\tgithub.com/jackc/pgx/v4 v4.18.3 // indirect\n\tgithub.com/jmoiron/sqlx v1.3.5 // indirect\n\tgithub.com/joho/godotenv v1.5.1 // indirect\n\tgithub.com/jonboulle/clockwork v0.5.0 // indirect\n\tgithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect\n\tgithub.com/knadh/koanf/maps v0.1.2 // indirect\n\tgithub.com/leodido/go-urn v1.4.0 // indirect\n\tgithub.com/lestrrat-go/blackmagic v1.0.3 // indirect\n\tgithub.com/lestrrat-go/httpcc v1.0.1 // indirect\n\tgithub.com/lestrrat-go/httprc v1.0.6 // indirect\n\tgithub.com/lestrrat-go/iter v1.0.2 // indirect\n\tgithub.com/lestrrat-go/option v1.0.1 // indirect\n\tgithub.com/luna-duclos/instrumentedsql v1.1.3 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/mattn/go-colorable v0.1.14 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.20 // indirect\n\tgithub.com/mitchellh/copystructure v1.2.0 // indirect\n\tgithub.com/mitchellh/reflectwalk v1.0.2 // indirect\n\tgithub.com/moby/docker-image-spec v1.3.1 // indirect\n\tgithub.com/moby/moby/api v1.53.0 // indirect\n\tgithub.com/moby/moby/client v0.2.2 // indirect\n\tgithub.com/moby/sys/user v0.4.0 // indirect\n\tgithub.com/moby/term v0.5.2 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.1 // indirect\n\tgithub.com/opencontainers/runc v1.2.8 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/prometheus/client_golang v1.22.0 // indirect\n\tgithub.com/prometheus/client_model v0.6.2 // indirect\n\tgithub.com/prometheus/common v0.63.0 // indirect\n\tgithub.com/prometheus/procfs v0.16.1 // indirect\n\tgithub.com/rogpeppe/go-internal v1.13.1 // indirect\n\tgithub.com/segmentio/asm v1.2.0 // indirect\n\tgithub.com/sergi/go-diff v1.2.0 // indirect\n\tgithub.com/sirupsen/logrus v1.9.3 // indirect\n\tgithub.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect\n\tgithub.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect\n\tgithub.com/spf13/pflag v1.0.9 // indirect\n\tgithub.com/tidwall/match v1.1.1 // indirect\n\tgithub.com/tidwall/pretty v1.2.0 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/valyala/fasttemplate v1.2.2 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect\n\tgithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect\n\tgithub.com/xeipuuv/gojsonschema v1.2.0 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.1.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect\n\tgo.opentelemetry.io/otel v1.35.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.35.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.35.0 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.org/x/mod v0.33.0 // indirect\n\tgolang.org/x/net v0.51.0 // indirect\n\tgolang.org/x/sync v0.20.0 // indirect\n\tgolang.org/x/sys v0.42.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.6 // indirect\n\tgopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n)\n"
  },
  {
    "path": "backend/go.sum",
    "content": "dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=\ndario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\nfilippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=\nfilippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=\ngithub.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=\ngithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=\ngithub.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=\ngithub.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=\ngithub.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY=\ngithub.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=\ngithub.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=\ngithub.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=\ngithub.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=\ngithub.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=\ngithub.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=\ngithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=\ngithub.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=\ngithub.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=\ngithub.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=\ngithub.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=\ngithub.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=\ngithub.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=\ngithub.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=\ngithub.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=\ngithub.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=\ngithub.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=\ngithub.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=\ngithub.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=\ngithub.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=\ngithub.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=\ngithub.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=\ngithub.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=\ngithub.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=\ngithub.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=\ngithub.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM=\ngithub.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=\ngithub.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=\ngithub.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=\ngithub.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=\ngithub.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=\ngithub.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=\ngithub.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=\ngithub.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=\ngithub.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=\ngithub.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=\ngithub.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\ngithub.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=\ngithub.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=\ngithub.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=\ngithub.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=\ngithub.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=\ngithub.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=\ngithub.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=\ngithub.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=\ngithub.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI=\ngithub.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=\ngithub.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=\ngithub.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=\ngithub.com/go-redsync/redsync/v4 v4.16.0 h1:bNcOzeHH9d3s6pghU9NJFMPrQa41f5Nx3L4YKr3BdEU=\ngithub.com/go-redsync/redsync/v4 v4.16.0/go.mod h1:V4gagqgyASWBZuwx4xGzu72aZNb/6Mo05byUa3mVmKQ=\ngithub.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-testfixtures/testfixtures/v3 v3.19.0 h1:/Y0bars250zggm+1A2PvwaJQsJel7/tS4D/Hhwt66Bc=\ngithub.com/go-testfixtures/testfixtures/v3 v3.19.0/go.mod h1:4/hVAuX2As0/ej3fLuAd+IvoCXV7/h2cj5nInI11uxM=\ngithub.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=\ngithub.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/go-webauthn/webauthn v0.10.2 h1:OG7B+DyuTytrEPFmTX503K77fqs3HDK/0Iv+z8UYbq4=\ngithub.com/go-webauthn/webauthn v0.10.2/go.mod h1:Gd1IDsGAybuvK1NkwUTLbGmeksxuRJjVN2PE/xsPxHs=\ngithub.com/go-webauthn/x v0.1.9 h1:v1oeLmoaa+gPOaZqUdDentu6Rl7HkSSsmOT6gxEQHhE=\ngithub.com/go-webauthn/x v0.1.9/go.mod h1:pJNMlIMP1SU7cN8HNlKJpLEnFHCygLCvaLZ8a1xeoQA=\ngithub.com/gobuffalo/attrs v1.0.3/go.mod h1:KvDJCE0avbufqS0Bw3UV7RQynESY0jjod+572ctX4t8=\ngithub.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4=\ngithub.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8=\ngithub.com/gobuffalo/fizz v1.14.4 h1:8uume7joF6niTNWN582IQ2jhGTUoa9g1fiV/tIoGdBs=\ngithub.com/gobuffalo/fizz v1.14.4/go.mod h1:9/2fGNXNeIFOXEEgTPJwiK63e44RjG+Nc4hfMm1ArGM=\ngithub.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=\ngithub.com/gobuffalo/flect v1.0.0 h1:eBFmskjXZgAOagiTXJH25Nt5sdFwNRcb8DKZsIsAUQI=\ngithub.com/gobuffalo/flect v1.0.0/go.mod h1:l9V6xSb4BlXwsxEMj3FVEub2nkdQjWhPvD8XTTlHPQc=\ngithub.com/gobuffalo/genny/v2 v2.1.0/go.mod h1:4yoTNk4bYuP3BMM6uQKYPvtP6WsXFGm2w2EFYZdRls8=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.3 h1:rSMPtx9ePkFB22vJ+dH+m/EUBS8doQ3S8LeEXcdwZHk=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.3/go.mod h1:IzgO5xS6hqkDmUh91BW/+Qxo/qYnvfzoz3A7uLkg77I=\ngithub.com/gobuffalo/helpers v0.6.7 h1:C9CedoRSfgWg2ZoIkVXgjI5kgmSpL34Z3qdnzpfNVd8=\ngithub.com/gobuffalo/helpers v0.6.7/go.mod h1:j0u1iC1VqlCaJEEVkZN8Ia3TEzfj/zoXANqyJExTMTA=\ngithub.com/gobuffalo/logger v1.0.7/go.mod h1:u40u6Bq3VVvaMcy5sRBclD8SXhBYPS0Qk95ubt+1xJM=\ngithub.com/gobuffalo/nulls v0.4.2 h1:GAqBR29R3oPY+WCC7JL9KKk9erchaNuV6unsOSZGQkw=\ngithub.com/gobuffalo/nulls v0.4.2/go.mod h1:EElw2zmBYafU2R9W4Ii1ByIj177wA/pc0JdjtD0EsH8=\ngithub.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8=\ngithub.com/gobuffalo/plush/v4 v4.1.16/go.mod h1:6t7swVsarJ8qSLw1qyAH/KbrcSTwdun2ASEQkOznakg=\ngithub.com/gobuffalo/plush/v4 v4.1.18 h1:bnPjdMTEUQHqj9TNX2Ck3mxEXYZa+0nrFMNM07kpX9g=\ngithub.com/gobuffalo/plush/v4 v4.1.18/go.mod h1:xi2tJIhFI4UdzIL8sxZtzGYOd2xbBpcFbLZlIPGGZhU=\ngithub.com/gobuffalo/pop/v6 v6.1.1 h1:eUDBaZcb0gYrmFnKwpuTEUA7t5ZHqNfvS4POqJYXDZY=\ngithub.com/gobuffalo/pop/v6 v6.1.1/go.mod h1:1n7jAmI1i7fxuXPZjZb0VBPQDbksRtCoFnrDV5IsvaI=\ngithub.com/gobuffalo/tags/v3 v3.1.4 h1:X/ydLLPhgXV4h04Hp2xlbI2oc5MDaa7eub6zw8oHjsM=\ngithub.com/gobuffalo/tags/v3 v3.1.4/go.mod h1:ArRNo3ErlHO8BtdA0REaZxijuWnWzF6PUXngmMXd2I0=\ngithub.com/gobuffalo/validate/v3 v3.3.3 h1:o7wkIGSvZBYBd6ChQoLxkz2y1pfmhbI4jNJYh6PuNJ4=\ngithub.com/gobuffalo/validate/v3 v3.3.3/go.mod h1:YC7FsbJ/9hW/VjQdmXPvFqvRis4vrRYFxr69WiNZw6g=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=\ngithub.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=\ngithub.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=\ngithub.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=\ngithub.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=\ngithub.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=\ngithub.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=\ngithub.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=\ngithub.com/gomodule/redigo v1.9.3/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=\ngithub.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=\ngithub.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=\ngithub.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=\ngithub.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=\ngithub.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=\ngithub.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=\ngithub.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=\ngithub.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=\ngithub.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=\ngithub.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=\ngithub.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=\ngithub.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=\ngithub.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=\ngithub.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=\ngithub.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=\ngithub.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=\ngithub.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=\ngithub.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=\ngithub.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=\ngithub.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=\ngithub.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=\ngithub.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=\ngithub.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=\ngithub.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=\ngithub.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=\ngithub.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=\ngithub.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=\ngithub.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=\ngithub.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8=\ngithub.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=\ngithub.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=\ngithub.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=\ngithub.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=\ngithub.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=\ngithub.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=\ngithub.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=\ngithub.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=\ngithub.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=\ngithub.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=\ngithub.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=\ngithub.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=\ngithub.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=\ngithub.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=\ngithub.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=\ngithub.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=\ngithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=\ngithub.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=\ngithub.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=\ngithub.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=\ngithub.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=\ngithub.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=\ngithub.com/knadh/koanf/parsers/json v1.0.0 h1:1pVR1JhMwbqSg5ICzU+surJmeBbdT4bQm7jjgnA+f8o=\ngithub.com/knadh/koanf/parsers/json v1.0.0/go.mod h1:zb5WtibRdpxSoSJfXysqGbVxvbszdlroWDHGdDkkEYU=\ngithub.com/knadh/koanf/parsers/yaml v1.1.0 h1:3ltfm9ljprAHt4jxgeYLlFPmUaunuCgu1yILuTXRdM4=\ngithub.com/knadh/koanf/parsers/yaml v1.1.0/go.mod h1:HHmcHXUrp9cOPcuC+2wrr44GTUB0EC+PyfN3HZD9tFg=\ngithub.com/knadh/koanf/providers/file v1.2.1 h1:bEWbtQwYrA+W2DtdBrQWyXqJaJSG3KrP3AESOJYp9wM=\ngithub.com/knadh/koanf/providers/file v1.2.1/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=\ngithub.com/knadh/koanf/v2 v2.3.4 h1:fnynNSDlujWE+v83hAp8wKr/cdoxHLO0629SN+U8Urc=\ngithub.com/knadh/koanf/v2 v2.3.4/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/labstack/echo-contrib v0.17.4 h1:g5mfsrJfJTKv+F5uNKCyrjLK7js+ZW6HTjg4FnDxxgk=\ngithub.com/labstack/echo-contrib v0.17.4/go.mod h1:9O7ZPAHUeMGTOAfg80YqQduHzt0CzLak36PZRldYrZ0=\ngithub.com/labstack/echo-jwt/v4 v4.4.0 h1:nrXaEnJupfc2R4XChcLRDyghhMZup77F8nIzHnBK19U=\ngithub.com/labstack/echo-jwt/v4 v4.4.0/go.mod h1:kYXWgWms9iFqI3ldR+HAEj/Zfg5rZtR7ePOgktG4Hjg=\ngithub.com/labstack/echo/v4 v4.15.1 h1:S9keusg26gZpjMmPqB5hOEvNKnmd1lNmcHrbbH2lnFs=\ngithub.com/labstack/echo/v4 v4.15.1/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=\ngithub.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=\ngithub.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=\ngithub.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=\ngithub.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=\ngithub.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=\ngithub.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=\ngithub.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=\ngithub.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=\ngithub.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=\ngithub.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=\ngithub.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=\ngithub.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=\ngithub.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA=\ngithub.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=\ngithub.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=\ngithub.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo=\ngithub.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=\ngithub.com/luna-duclos/instrumentedsql v1.1.3 h1:t7mvC0z1jUt5A0UQ6I/0H31ryymuQRnJcWCiqV3lSAA=\ngithub.com/luna-duclos/instrumentedsql v1.1.3/go.mod h1:9J1njvFds+zN7y85EDhN9XNQLANWwZt2ULeIC8yMNYs=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=\ngithub.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=\ngithub.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=\ngithub.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y=\ngithub.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=\ngithub.com/mileusna/useragent v1.3.5 h1:SJM5NzBmh/hO+4LGeATKpaEX9+b4vcGg2qXGLiNGDws=\ngithub.com/mileusna/useragent v1.3.5/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=\ngithub.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=\ngithub.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=\ngithub.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=\ngithub.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=\ngithub.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=\ngithub.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=\ngithub.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM=\ngithub.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ=\ngithub.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=\ngithub.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=\ngithub.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=\ngithub.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=\ngithub.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=\ngithub.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd17NIEQ=\ngithub.com/nicksnyder/go-i18n/v2 v2.6.1/go.mod h1:Vee0/9RD3Quc/NmwEjzzD7VTZ+Ir7QbXocrkhOzmUKA=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=\ngithub.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=\ngithub.com/opencontainers/runc v1.2.8 h1:RnEICeDReapbZ5lZEgHvj7E9Q3Eex9toYmaGBsbvU5Q=\ngithub.com/opencontainers/runc v1.2.8/go.mod h1:cC0YkmZcuvr+rtBZ6T7NBoVbMGNAdLa/21vIElJDOzI=\ngithub.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw=\ngithub.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=\ngithub.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=\ngithub.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=\ngithub.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=\ngithub.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=\ngithub.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=\ngithub.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=\ngithub.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=\ngithub.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=\ngithub.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=\ngithub.com/redis/go-redis/v9 v9.17.3 h1:fN29NdNrE17KttK5Ndf20buqfDZwGNgoUr9qjl1DQx4=\ngithub.com/redis/go-redis/v9 v9.17.3/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=\ngithub.com/redis/rueidis v1.0.71 h1:pODtnAR5GAB7j4ekhldZ29HKOxe4Hph0GTDGk1ayEQY=\ngithub.com/redis/rueidis v1.0.71/go.mod h1:lfdcZzJ1oKGKL37vh9fO3ymwt+0TdjkkUCJxbgpmcgQ=\ngithub.com/redis/rueidis/rueidiscompat v1.0.71 h1:wNZ//kEjMZgBM0KCk7ncOX8KmAgROU2kDdDNpwheG4w=\ngithub.com/redis/rueidis/rueidiscompat v1.0.71/go.mod h1:esmCLJvaRzZoKlgB82G1bY7Iky5TnO9Rz+NlhbEccFI=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=\ngithub.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=\ngithub.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=\ngithub.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=\ngithub.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=\ngithub.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=\ngithub.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=\ngithub.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=\ngithub.com/russellhaering/gosaml2 v0.11.0 h1:wlWm7dWMrpJBzh0xEOZof70nVen4f/2BEF8ZXaidJ9o=\ngithub.com/russellhaering/gosaml2 v0.11.0/go.mod h1:GmL5LeCP7PBYzSkkFxtmHuRzC2eUZ/6JSLYQd5fzKK4=\ngithub.com/russellhaering/goxmldsig v1.6.0 h1:8fdWXEPh2k/NZNQBPFNoVfS3JmzS4ZprY/sAOpKQLks=\ngithub.com/russellhaering/goxmldsig v1.6.0/go.mod h1:TrnaquDcYxWXfJrOjeMBTX4mLBeYAqaHEyUeWPxZlBM=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=\ngithub.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=\ngithub.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=\ngithub.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/sethvargo/go-limiter v0.6.0/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU=\ngithub.com/sethvargo/go-limiter v1.1.0 h1:eLeZVQ2zqJOiEs03GguqmBVG6/T6lsZB+6PP1t7J6fA=\ngithub.com/sethvargo/go-limiter v1.1.0/go.mod h1:01b6tW25Ap+MeLYBuD4aHunMrJoNO5PVUFdS9rac3II=\ngithub.com/sethvargo/go-redisstore v0.3.0 h1:yCDGc7ERWfa9BMgjhMhYcH8k+y85bRx0nziupGhjPkc=\ngithub.com/sethvargo/go-redisstore v0.3.0/go.mod h1:rY+FgiPpRrdpi4wETGHdMf6YlJnGiziAt2R8gXaFFxg=\ngithub.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=\ngithub.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=\ngithub.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=\ngithub.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E=\ngithub.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=\ngithub.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8=\ngithub.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=\ngithub.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=\ngithub.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=\ngithub.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=\ngithub.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=\ngithub.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=\ngithub.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=\ngithub.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=\ngithub.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=\ngithub.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=\ngithub.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=\ngithub.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=\ngithub.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=\ngithub.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=\ngo.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=\ngo.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=\ngo.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=\ngo.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=\ngo.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=\ngo.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=\ngo.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=\ngo.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=\ngo.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=\ngo.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=\ngo.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=\ngo.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=\ngolang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=\ngolang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=\ngolang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=\ngolang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=\ngolang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=\ngolang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=\ngolang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=\ngolang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=\ngolang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=\ngolang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=\ngoogle.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=\ngopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=\ngopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=\ngopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=\ngopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=\ngotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\npgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=\npgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=\n"
  },
  {
    "path": "backend/handler/admin_router.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/labstack/echo-contrib/echoprometheus\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/labstack/echo/v4/middleware\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\thankoMiddleware \"github.com/teamhanko/hanko/backend/v2/middleware\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/template\"\n)\n\nfunc NewAdminRouter(cfg *config.Config, persister persistence.Persister, prometheus echo.MiddlewareFunc) *echo.Echo {\n\te := echo.New()\n\te.Renderer = template.NewTemplateRenderer()\n\te.HideBanner = true\n\tg := e.Group(\"\")\n\n\te.HTTPErrorHandler = dto.NewHTTPErrorHandler(dto.HTTPErrorHandlerConfig{Debug: false, Logger: e.Logger})\n\te.Use(middleware.RequestID())\n\tif cfg.Log.LogHealthAndMetrics {\n\t\te.Use(hankoMiddleware.GetLoggerMiddleware())\n\t} else {\n\t\tg.Use(hankoMiddleware.GetLoggerMiddleware())\n\t}\n\n\te.Validator = dto.NewCustomValidator()\n\n\tif prometheus != nil {\n\t\te.Use(prometheus)\n\t\te.GET(\"/metrics\", echoprometheus.NewHandler())\n\t}\n\n\tstatusHandler := NewStatusHandler(persister)\n\n\te.GET(\"/\", statusHandler.Status)\n\n\thealthHandler := NewHealthHandler()\n\n\thealth := e.Group(\"/health\")\n\thealth.GET(\"/alive\", healthHandler.Alive)\n\thealth.GET(\"/ready\", healthHandler.Ready)\n\n\tjwkManager, err := jwk.NewManager(cfg.Secrets, persister)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to create jwk manager: %w\", err))\n\t}\n\tsessionManager, err := session.NewManager(jwkManager, *cfg)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to create session generator: %w\", err))\n\t}\n\n\twebhookMiddleware := hankoMiddleware.WebhookMiddleware(cfg, jwkManager, persister)\n\tauditLogger := auditlog.NewLogger(persister, cfg.AuditLog)\n\n\tuserHandler := NewUserHandlerAdmin(persister)\n\temailHandler := NewEmailAdminHandler(cfg, persister)\n\tsessionsHandler := NewSessionAdminHandler(cfg, persister, sessionManager, auditLogger)\n\n\tuser := g.Group(\"/users\")\n\tuser.GET(\"\", userHandler.List)\n\tuser.POST(\"\", userHandler.Create, webhookMiddleware)\n\tuser.GET(\"/:id\", userHandler.Get)\n\tuser.DELETE(\"/:id\", userHandler.Delete, webhookMiddleware)\n\tuser.PATCH(\"/:id\", userHandler.Patch)\n\n\tmetadataHandler := NewMetadataAdminHandler(persister)\n\tuser.PATCH(\"/:id/metadata\", metadataHandler.PatchMetadata)\n\tuser.GET(\"/:id/metadata\", metadataHandler.GetMetadata)\n\n\temail := user.Group(\"/:user_id/emails\", webhookMiddleware)\n\temail.GET(\"\", emailHandler.List)\n\temail.POST(\"\", emailHandler.Create)\n\temail.GET(\"/:email_id\", emailHandler.Get)\n\temail.DELETE(\"/:email_id\", emailHandler.Delete)\n\temail.POST(\"/:email_id/set_primary\", emailHandler.SetPrimaryEmail)\n\n\twebauthnCredentialHandler := NewWebauthnCredentialAdminHandler(persister)\n\twebauthnCredentials := user.Group(\"/:user_id/webauthn_credentials\")\n\twebauthnCredentials.GET(\"\", webauthnCredentialHandler.List)\n\twebauthnCredentials.GET(\"/:credential_id\", webauthnCredentialHandler.Get)\n\twebauthnCredentials.DELETE(\"/:credential_id\", webauthnCredentialHandler.Delete)\n\n\tpasswordCredentialHandler := NewPasswordAdminHandler(persister)\n\tpasswordCredentials := user.Group(\"/:user_id/password\")\n\tpasswordCredentials.GET(\"\", passwordCredentialHandler.Get)\n\tpasswordCredentials.POST(\"\", passwordCredentialHandler.Create)\n\tpasswordCredentials.PUT(\"\", passwordCredentialHandler.Update)\n\tpasswordCredentials.DELETE(\"\", passwordCredentialHandler.Delete)\n\n\tuserSessions := user.Group(\"/:user_id/sessions\")\n\tuserSessions.GET(\"\", sessionsHandler.List)\n\tuserSessions.DELETE(\"/:session_id\", sessionsHandler.Delete)\n\n\totpHandler := NewOTPAdminHandler(persister)\n\totp := user.Group(\"/:user_id/otp\")\n\totp.GET(\"\", otpHandler.Get)\n\totp.DELETE(\"\", otpHandler.Delete)\n\n\tauditLogHandler := NewAuditLogHandler(persister)\n\n\tauditLogs := g.Group(\"/audit_logs\")\n\tauditLogs.GET(\"\", auditLogHandler.List)\n\n\twebhookHandler := NewWebhookHandler(cfg.Webhooks, persister)\n\twebhooks := g.Group(\"/webhooks\")\n\twebhooks.GET(\"\", webhookHandler.List)\n\twebhooks.POST(\"\", webhookHandler.Create)\n\twebhooks.GET(\"/:id\", webhookHandler.Get)\n\twebhooks.DELETE(\"/:id\", webhookHandler.Delete)\n\twebhooks.PUT(\"/:id\", webhookHandler.Update)\n\n\tsessions := g.Group(\"/sessions\")\n\tsessions.POST(\"\", sessionsHandler.Generate)\n\n\treturn e\n}\n"
  },
  {
    "path": "backend/handler/audit_log.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/pagination\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"time\"\n)\n\ntype AuditLogHandler struct {\n\tpersister persistence.Persister\n}\n\nfunc NewAuditLogHandler(persister persistence.Persister) *AuditLogHandler {\n\treturn &AuditLogHandler{\n\t\tpersister: persister,\n\t}\n}\n\ntype AuditLogListRequest struct {\n\tPage         int        `query:\"page\"`\n\tPerPage      int        `query:\"per_page\"`\n\tStartTime    *time.Time `query:\"start_time\"`\n\tEndTime      *time.Time `query:\"end_time\"`\n\tTypes        []string   `query:\"type\"`\n\tUserId       string     `query:\"actor_user_id\"`\n\tEmail        string     `query:\"actor_email\"`\n\tIP           string     `query:\"meta_source_ip\"`\n\tSearchString string     `query:\"q\"`\n}\n\nfunc (h AuditLogHandler) List(c echo.Context) error {\n\tvar request AuditLogListRequest\n\terr := (&echo.DefaultBinder{}).BindQueryParams(c, &request)\n\tif err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif request.Page == 0 {\n\t\trequest.Page = 1\n\t}\n\n\tif request.PerPage == 0 {\n\t\trequest.PerPage = 20\n\t}\n\n\tauditLogs, err := h.persister.GetAuditLogPersister().List(request.Page, request.PerPage, request.StartTime, request.EndTime, request.Types, request.UserId, request.Email, request.IP, request.SearchString)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get list of audit logs: %w\", err)\n\t}\n\n\tlogCount, err := h.persister.GetAuditLogPersister().Count(request.StartTime, request.EndTime, request.Types, request.UserId, request.Email, request.IP, request.SearchString)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get total count of audit logs: %w\", err)\n\t}\n\n\tu, _ := url.Parse(fmt.Sprintf(\"%s://%s%s\", c.Scheme(), c.Request().Host, c.Request().RequestURI))\n\n\tc.Response().Header().Set(\"Link\", pagination.CreateHeader(u, logCount, request.Page, request.PerPage))\n\tc.Response().Header().Set(\"X-Total-Count\", strconv.FormatInt(int64(logCount), 10))\n\n\treturn c.JSON(http.StatusOK, auditLogs)\n}\n"
  },
  {
    "path": "backend/handler/email.go",
    "content": "package handler\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype EmailHandler struct {\n\tpersister   persistence.Persister\n\tcfg         *config.Config\n\tauditLogger auditlog.Logger\n}\n\nfunc NewEmailHandler(cfg *config.Config, persister persistence.Persister, auditLogger auditlog.Logger) *EmailHandler {\n\treturn &EmailHandler{\n\t\tpersister:   persister,\n\t\tcfg:         cfg,\n\t\tauditLogger: auditLogger,\n\t}\n}\n\nfunc (h *EmailHandler) List(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"failed to cast session object\")\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse subject as uuid: %w\", err)\n\t}\n\n\temails, err := h.persister.GetEmailPersister().FindByUserId(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch emails from db: %w\", err)\n\t}\n\n\tresponse := make([]*dto.EmailResponse, len(emails))\n\n\tfor i := range emails {\n\t\tresponse[i] = dto.FromEmailModel(&emails[i], h.cfg)\n\t}\n\n\treturn c.JSON(http.StatusOK, response)\n}\n\nfunc (h *EmailHandler) Create(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"failed to cast session object\")\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse subject as uuid: %w\", err)\n\t}\n\n\tvar body dto.EmailCreateRequest\n\n\terr = (&echo.DefaultBinder{}).BindBody(c, &body)\n\tif err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\temailCount, err := h.persister.GetEmailPersister().CountByUserId(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to count user emails: %w\", err)\n\t}\n\n\tif emailCount >= h.cfg.Email.Limit {\n\t\treturn echo.NewHTTPError(http.StatusConflict).SetInternal(errors.New(\"max number of email addresses reached\"))\n\t}\n\n\tnewEmailAddress := strings.ToLower(body.Address)\n\n\temail, err := h.persister.GetEmailPersister().FindByAddress(newEmailAddress)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch email from db: %w\", err)\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tuser, err := h.persister.GetUserPersister().Get(userId)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to fetch user from db: %w\", err)\n\t\t}\n\n\t\tif email != nil {\n\t\t\t// The email address already exists.\n\t\t\tif email.UserID != nil {\n\t\t\t\t// The email address exists and is assigned to a user already, therefore it can't be created.\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest).SetInternal(errors.New(\"email address already exists\"))\n\t\t\t}\n\n\t\t\tif !h.cfg.Email.RequireVerification {\n\t\t\t\t// Email verification is currently not required and there is no user assigned to the existing email\n\t\t\t\t// address. This can happen, when email verification was turned on before, because then the email\n\t\t\t\t// address will be assigned to the user only after passcode verification. The email was left unassigned\n\t\t\t\t// and has not been verified, so we assign the email to the current user.\n\t\t\t\temail.UserID = &user.ID\n\n\t\t\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Update(*email)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to update the existing email: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// The email address has not been registered so far.\n\t\t\tif h.cfg.Email.RequireVerification {\n\t\t\t\t// The email address will be assigned to the user only after passcode verification.\n\t\t\t\temail = models.NewEmail(nil, newEmailAddress)\n\t\t\t} else {\n\t\t\t\t// No verification required - assign the email to the given user.\n\t\t\t\temail = models.NewEmail(&user.ID, newEmailAddress)\n\t\t\t}\n\n\t\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Create(*email)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to store email to db: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogEmailCreated, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\n\t\tif !h.cfg.Email.RequireVerification {\n\t\t\tvar evt events.Event\n\n\t\t\tif len(user.Emails) >= 1 {\n\t\t\t\tevt = events.UserEmailCreate\n\t\t\t} else {\n\t\t\t\tevt = events.UserCreate\n\t\t\t}\n\n\t\t\tutils.NotifyUserChange(c, tx, h.persister, evt, userId)\n\n\t\t}\n\n\t\treturn c.JSON(http.StatusOK, email)\n\t})\n}\n\nfunc (h *EmailHandler) SetPrimaryEmail(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"failed to cast session object\")\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse subject as uuid: %w\", err)\n\t}\n\n\temailId, err := uuid.FromString(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest).SetInternal(err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch user from db: %w\", err)\n\t}\n\n\temail := user.GetEmailById(emailId)\n\tif email == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"the email address is not assigned to the current user\"))\n\t}\n\n\tif email.IsPrimary() {\n\t\treturn c.NoContent(http.StatusNoContent)\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tvar primaryEmail *models.PrimaryEmail\n\t\tif e := user.Emails.GetPrimary(); e != nil {\n\t\t\tprimaryEmail = e.PrimaryEmail\n\t\t}\n\n\t\tif primaryEmail == nil {\n\t\t\tprimaryEmail = models.NewPrimaryEmail(email.ID, user.ID)\n\t\t\terr = h.persister.GetPrimaryEmailPersisterWithConnection(tx).Create(*primaryEmail)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to store new primary email: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\tprimaryEmail.EmailID = email.ID\n\t\t\terr = h.persister.GetPrimaryEmailPersisterWithConnection(tx).Update(*primaryEmail)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to change primary email: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPrimaryEmailChanged, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\n\t\tutils.NotifyUserChange(c, tx, h.persister, events.UserEmailPrimary, userId)\n\n\t\treturn c.NoContent(http.StatusNoContent)\n\t})\n}\n\nfunc (h *EmailHandler) Delete(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"failed to cast session object\")\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse subject as uuid: %w\", err)\n\t}\n\n\temailId, err := uuid.FromString(c.Param(\"id\"))\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch user from db: %w\", err)\n\t}\n\n\temailToBeDeleted := user.GetEmailById(emailId)\n\tif emailToBeDeleted == nil {\n\t\treturn errors.New(\"email with given emailId not available\")\n\t}\n\n\tif emailToBeDeleted.IsPrimary() {\n\t\treturn echo.NewHTTPError(http.StatusConflict).SetInternal(errors.New(\"primary email can't be deleted\"))\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Delete(*emailToBeDeleted)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete email from db: %w\", err)\n\t\t}\n\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogEmailDeleted, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\n\t\tutils.NotifyUserChange(c, tx, h.persister, events.UserEmailDelete, userId)\n\n\t\treturn c.NoContent(http.StatusNoContent)\n\t})\n}\n"
  },
  {
    "path": "backend/handler/email_admin.go",
    "content": "package handler\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype EmailAdminHandler interface {\n\tList(ctx echo.Context) error\n\tCreate(ctx echo.Context) error\n\n\tGet(ctx echo.Context) error\n\tDelete(ctx echo.Context) error\n\tSetPrimaryEmail(ctx echo.Context) error\n}\n\ntype emailAdminHandler struct {\n\tcfg       *config.Config\n\tpersister persistence.Persister\n}\n\nconst (\n\tparseUserUuidFailureMessage   = \"failed to parse user_id as uuid: %w\"\n\tfetchUserFromDbFailureMessage = \"failed to fetch user from db: %w\"\n)\n\nfunc NewEmailAdminHandler(cfg *config.Config, persister persistence.Persister) EmailAdminHandler {\n\treturn &emailAdminHandler{\n\t\tcfg:       cfg,\n\t\tpersister: persister,\n\t}\n}\n\nfunc (h *emailAdminHandler) List(ctx echo.Context) error {\n\tlistDto, err := loadDto[admin.ListEmailRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserId, err := uuid.FromString(listDto.UserId)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\temails, err := h.persister.GetEmailPersister().FindByUserId(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch emails from db: %w\", err)\n\t}\n\n\tresponse := make([]*admin.Email, len(emails))\n\n\tfor i := range emails {\n\t\tresponse[i] = admin.FromEmailModel(&emails[i])\n\t}\n\n\treturn ctx.JSON(http.StatusOK, response)\n}\n\nfunc (h *emailAdminHandler) Create(ctx echo.Context) error {\n\tcreateDto, err := loadDto[admin.CreateEmailRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserId, err := uuid.FromString(createDto.UserId)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\temailCount, err := h.persister.GetEmailPersister().CountByUserId(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to count user emails: %w\", err)\n\t}\n\n\tif emailCount >= h.cfg.Email.Limit {\n\t\treturn echo.NewHTTPError(http.StatusConflict).SetInternal(errors.New(\"max number of email addresses reached\"))\n\t}\n\n\tnewEmailAddress := strings.ToLower(createDto.Address)\n\n\temail, err := h.persister.GetEmailPersister().FindByAddress(newEmailAddress)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch email from db: %w\", err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(fetchUserFromDbFailureMessage, err)\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tif user == nil {\n\t\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"user not found\"))\n\t\t}\n\n\t\tif email != nil {\n\t\t\t// The email address already exists.\n\t\t\tif email.UserID != nil {\n\t\t\t\t// The email address exists and is assigned to a user already, therefore it can't be created.\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest).SetInternal(errors.New(\"email address already exists\"))\n\t\t\t}\n\n\t\t\temail.UserID = &user.ID\n\t\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Update(*email)\n\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to update the existing email: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\temail = models.NewEmail(&user.ID, newEmailAddress)\n\t\t\temail.Verified = createDto.IsVerified\n\n\t\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Create(*email)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to store email to db: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\t// make email primary if user had no emails prior to email creation\n\t\tif len(user.Emails) == 0 {\n\t\t\tprimaryEmail := models.NewPrimaryEmail(email.ID, user.ID)\n\t\t\terr = h.persister.GetPrimaryEmailPersisterWithConnection(tx).Create(*primaryEmail)\n\t\t}\n\n\t\tutils.NotifyUserChange(ctx, tx, h.persister, events.UserEmailCreate, userId)\n\n\t\treturn ctx.JSON(http.StatusCreated, admin.FromEmailModel(email))\n\t})\n}\n\nfunc (h *emailAdminHandler) Get(ctx echo.Context) error {\n\tgetDto, err := loadDto[admin.GetEmailRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserId, err := uuid.FromString(getDto.UserId)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\temailId, err := uuid.FromString(getDto.EmailId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse email_id as uuid: %w\", err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(fetchUserFromDbFailureMessage, err)\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf(\"user with id '%s' was not found\", userId))\n\t}\n\n\tfetchedEmail := user.GetEmailById(emailId)\n\tif fetchedEmail == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, fmt.Errorf(\"email with id '%s' was not found\", emailId))\n\t}\n\n\treturn ctx.JSON(http.StatusOK, admin.FromEmailModel(fetchedEmail))\n}\n\nfunc (h *emailAdminHandler) Delete(ctx echo.Context) error {\n\tdeleteDto, err := loadDto[admin.GetEmailRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserId, err := uuid.FromString(deleteDto.UserId)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\temailId, err := uuid.FromString(deleteDto.EmailId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse email_id as uuid: %w\", err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(fetchUserFromDbFailureMessage, err)\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf(\"user with id '%s' was not found\", userId))\n\t}\n\n\temailToBeDeleted := user.GetEmailById(emailId)\n\tif emailToBeDeleted == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf(\"email with id '%s' was not found\", emailId))\n\t}\n\n\tif emailToBeDeleted.IsPrimary() {\n\t\treturn echo.NewHTTPError(http.StatusConflict).SetInternal(errors.New(\"primary email can't be deleted\"))\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Delete(*emailToBeDeleted)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete email from db: %w\", err)\n\t\t}\n\n\t\tutils.NotifyUserChange(ctx, tx, h.persister, events.UserEmailDelete, userId)\n\n\t\treturn ctx.NoContent(http.StatusNoContent)\n\t})\n}\n\nfunc (h *emailAdminHandler) SetPrimaryEmail(ctx echo.Context) error {\n\tprimaryDto, err := loadDto[admin.GetEmailRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserId, err := uuid.FromString(primaryDto.UserId)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\temailId, err := uuid.FromString(primaryDto.EmailId)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest).SetInternal(err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(fetchUserFromDbFailureMessage, err)\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf(\"user with id '%s' was not found\", userId))\n\t}\n\n\temail := user.GetEmailById(emailId)\n\tif email == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"the email address is not assigned to the current user\"))\n\t}\n\n\tif email.IsPrimary() {\n\t\treturn ctx.NoContent(http.StatusNoContent)\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\terr := h.makeEmailPrimary(ctx, email, user, tx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tutils.NotifyUserChange(ctx, tx, h.persister, events.UserEmailPrimary, userId)\n\n\t\treturn ctx.NoContent(http.StatusNoContent)\n\t})\n}\n\nfunc (h *emailAdminHandler) makeEmailPrimary(ctx echo.Context, email *models.Email, user *models.User, tx *pop.Connection) error {\n\tvar primaryEmail *models.PrimaryEmail\n\tif e := user.Emails.GetPrimary(); e != nil {\n\t\tprimaryEmail = e.PrimaryEmail\n\t}\n\n\tif primaryEmail == nil {\n\t\tprimaryEmail = models.NewPrimaryEmail(email.ID, user.ID)\n\t\terr := h.persister.GetPrimaryEmailPersisterWithConnection(tx).Create(*primaryEmail)\n\t\tif err != nil {\n\t\t\tctx.Logger().Error(err)\n\t\t\treturn fmt.Errorf(\"failed to store new primary email: %w\", err)\n\t\t}\n\t} else {\n\t\tprimaryEmail.EmailID = email.ID\n\t\terr := h.persister.GetPrimaryEmailPersisterWithConnection(tx).Update(*primaryEmail)\n\t\tif err != nil {\n\t\t\tctx.Logger().Error(err)\n\t\t\treturn fmt.Errorf(\"failed to change primary email: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/handler/email_admin_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestEmailAdminSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(emailAdminSuite))\n}\n\ntype emailAdminSuite struct {\n\ttest.Suite\n}\n\nfunc (s *emailAdminSuite) TestEmailAdminHandler_New() {\n\temailHandler := NewEmailAdminHandler(&config.Config{}, s.Storage)\n\ts.NotEmpty(emailHandler)\n}\n\nfunc (s *emailAdminSuite) TestEmailAdminHandler_List() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\texpectedCount      int\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:          \"should return all user assigned email addresses\",\n\t\t\tuserId:        \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedCount: 3,\n\t\t},\n\t\t{\n\t\t\tname:          \"should return an empty list when the user has no email addresses assigned\",\n\t\t\tuserId:        \"d41df4b7-c055-45e6-9faf-61aa92a4032e\",\n\t\t\texpectedCount: 0,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid\",\n\t\t\tuserId:             \"d41df4b7-c055-45e6-9faf-61aa92a4032\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to find non existing user\",\n\t\t\tuserId:             \"d41df4b7-c055-45e6-9faf-61aa92a4032f\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ttestDto := admin.ListEmailRequestDto{\n\t\t\t\tUserId: currentTest.userId,\n\t\t\t}\n\t\t\ttestJson, err := json.Marshal(testDto)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s/emails\", currentTest.userId), bytes.NewReader(testJson))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar emails []*dto.EmailResponse\n\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &emails))\n\t\t\t\ts.Equal(currentTest.expectedCount, len(emails))\n\t\t\t} else {\n\t\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *emailAdminSuite) TestEmailAdminHandler_Create() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\ttests := []struct {\n\t\tname                 string\n\t\temail                string\n\t\tuserId               string\n\t\tmaxNumberOfAddresses int\n\t\tisVerified           bool\n\t\texpectedStatusCode   int\n\t}{\n\t\t{\n\t\t\tname:                 \"should reject the request when the user has already reached the maximum number of email addresses\",\n\t\t\temail:                \"rejection1@example.com\",\n\t\t\tuserId:               \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tmaxNumberOfAddresses: 0,\n\t\t\tisVerified:           false,\n\t\t\texpectedStatusCode:   http.StatusConflict,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should error if email address is already in use\",\n\t\t\temail:                \"john.doe@example.com\",\n\t\t\tuserId:               \"d41df4b7-c055-45e6-9faf-61aa92a4032e\",\n\t\t\tisVerified:           false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should assign the email address to the user if not yet assigned and does not require verification\",\n\t\t\temail:                \"john.doe+6@example.com\",\n\t\t\tuserId:               \"d41df4b7-c055-45e6-9faf-61aa92a4032e\",\n\t\t\tisVerified:           false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusCreated,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should create email record with nil user ID if verification required\",\n\t\t\temail:                \"test.email.verification@example.com\",\n\t\t\tuserId:               \"d41df4b7-c055-45e6-9faf-61aa92a4032e\",\n\t\t\tisVerified:           true,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusCreated,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should create email record with user ID if verification not required\",\n\t\t\temail:                \"test.email.noverification@example.com\",\n\t\t\tuserId:               \"d41df4b7-c055-45e6-9faf-61aa92a4032e\",\n\t\t\tisVerified:           false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusCreated,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should fail to create email record with missing user id\",\n\t\t\temail:                \"test.email.noverification@example.com\",\n\t\t\tuserId:               \"\",\n\t\t\tisVerified:           false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should fail to create email record with non uuid user id\",\n\t\t\temail:                \"test.email.noverification@example.com\",\n\t\t\tuserId:               \"lorem\",\n\t\t\tisVerified:           false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should fail to create email record with wrong user id\",\n\t\t\temail:                \"test.email.noverification@example.com\",\n\t\t\tuserId:               \"d41df4b7-c055-45e6-9faf-61aa92a4032f\",\n\t\t\tisVerified:           false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should create verified email record with wrong user id\",\n\t\t\temail:                \"test.email.noverification@example.com\",\n\t\t\tuserId:               \"d41df4b7-c055-45e6-9faf-61aa92a4032e\",\n\t\t\tisVerified:           true,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should fail to create email with missing email\",\n\t\t\temail:                \"\",\n\t\t\tuserId:               \"d41df4b7-c055-45e6-9faf-61aa92a4032e\",\n\t\t\tisVerified:           true,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\tcfg := test.DefaultConfig\n\t\t\tcfg.Email.Limit = currentTest.maxNumberOfAddresses\n\n\t\t\te := NewAdminRouter(&cfg, s.Storage, nil)\n\n\t\t\tbody := admin.CreateEmailRequestDto{\n\t\t\t\tListEmailRequestDto: admin.ListEmailRequestDto{\n\t\t\t\t\tUserId: currentTest.userId,\n\t\t\t\t},\n\t\t\t\tCreateEmail: admin.CreateEmail{\n\t\t\t\t\tAddress:    currentTest.email,\n\t\t\t\t\tIsVerified: currentTest.isVerified,\n\t\t\t\t},\n\t\t\t}\n\t\t\tbodyJson, err := json.Marshal(body)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, fmt.Sprintf(\"/users/%s/emails\", currentTest.userId), bytes.NewReader(bodyJson))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif rec.Code == http.StatusOK {\n\t\t\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(currentTest.email)\n\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\tif email != nil {\n\t\t\t\t\ts.Equal(currentTest.userId, email.UserID.String())\n\t\t\t\t\ts.Equal(currentTest.isVerified, email.Verified)\n\t\t\t\t}\n\t\t\t}\n\n\t\t})\n\t}\n}\n\nfunc (s *emailAdminSuite) TestEmailAdminHandler_Get() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\ttests := []struct {\n\t\tname               string\n\t\temailId            string\n\t\tuserId             string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should get a single email\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to get an email for a non existent user\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a6\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to get an email for a wrong user UUID\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"lorem\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to get an email for an empty user UUID\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to get a non existent email\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1ff\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to get an empty email uuid\",\n\t\t\temailId:            \"\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to get a wrong email uuid\",\n\t\t\temailId:            \"lorem\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\tcfg := test.DefaultConfig\n\n\t\t\te := NewAdminRouter(&cfg, s.Storage, nil)\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s/emails/%s\", currentTest.userId, currentTest.emailId), nil)\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif rec.Code == http.StatusOK {\n\t\t\t\tvar email admin.Email\n\t\t\t\terr := json.Unmarshal(rec.Body.Bytes(), &email)\n\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\ts.Require().NotNil(email)\n\t\t\t\ts.Require().Equal(currentTest.emailId, email.ID.String())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *emailAdminSuite) TestEmailAdminHandler_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\ttests := []struct {\n\t\tname               string\n\t\temailId            string\n\t\tuserId             string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should delete an email\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to delete an email for a non existent user\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a6\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to delete an email for a wrong user UUID\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"lorem\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to delete an email for an empty user UUID\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to delete a non existent email\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1ff\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to delete an empty email uuid\",\n\t\t\temailId:            \"\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to delete a wrong email uuid\",\n\t\t\temailId:            \"lorem\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to delete a primary email\",\n\t\t\temailId:            \"51b7c175-ceb6-45ba-aae6-0092221c1b84\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusConflict,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\tcfg := test.DefaultConfig\n\n\t\t\te := NewAdminRouter(&cfg, s.Storage, nil)\n\n\t\t\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s/emails/%s\", currentTest.userId, currentTest.emailId), nil)\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif rec.Code == http.StatusOK {\n\t\t\t\tvar email admin.Email\n\t\t\t\terr := json.Unmarshal(rec.Body.Bytes(), &email)\n\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\ts.Require().NotNil(email)\n\t\t\t\ts.Require().Equal(currentTest.emailId, email.ID.String())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *emailAdminSuite) TestEmailAdminHandler_SetPrimaryEmail() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\ttests := []struct {\n\t\tname               string\n\t\temailId            string\n\t\tuserId             string\n\t\tisAlreadyPrimary   bool\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should set an email as primary\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tisAlreadyPrimary:   false,\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to set an email as primary for a non existent user\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a6\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to set an email as primary for a wrong user UUID\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"lorem\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to set an email as primary for an empty user UUID\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to set an email as primary for a non existent email\",\n\t\t\temailId:            \"f194ee0f-dd1a-48f7-8766-c67e4d6cd1ff\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to set an email as primary for an empty email uuid\",\n\t\t\temailId:            \"\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail to set an email as primary for a wrong email uuid\",\n\t\t\temailId:            \"lorem\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"Should not change a primary email\",\n\t\t\temailId:            \"51b7c175-ceb6-45ba-aae6-0092221c1b84\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\tcfg := test.DefaultConfig\n\n\t\t\te := NewAdminRouter(&cfg, s.Storage, nil)\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, fmt.Sprintf(\"/users/%s/emails/%s/set_primary\", currentTest.userId, currentTest.emailId), nil)\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif rec.Code == http.StatusNoContent {\n\t\t\t\tuserUuid, err := uuid.FromString(currentTest.userId)\n\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\temails, err := s.Storage.GetEmailPersister().FindByUserId(userUuid)\n\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\ts.Equal(3, len(emails))\n\t\t\t\tfor _, email := range emails {\n\t\t\t\t\tif email.ID.String() == currentTest.emailId {\n\t\t\t\t\t\ts.True(email.IsPrimary())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/email_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestEmailSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(emailSuite))\n}\n\ntype emailSuite struct {\n\ttest.Suite\n}\n\nfunc (s *emailSuite) TestEmailHandler_New() {\n\temailHandler := NewEmailHandler(&config.Config{}, s.Storage, test.NewAuditLogger())\n\ts.NotEmpty(emailHandler)\n}\n\nfunc (s *emailSuite) TestEmailHandler_List() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\ttests := []struct {\n\t\tname          string\n\t\tuserId        uuid.UUID\n\t\texpectedCount int\n\t}{\n\t\t{\n\t\t\tname:          \"should return all user assigned email addresses\",\n\t\t\tuserId:        uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\"),\n\t\t\texpectedCount: 3,\n\t\t},\n\t\t{\n\t\t\tname:          \"should return an empty list when the user has no email addresses assigned\",\n\t\t\tuserId:        uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t\texpectedCount: 0,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\tcookie, err := generateSessionCookie(s.Storage, currentTest.userId)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, \"/emails\", nil)\n\t\t\treq.AddCookie(cookie)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif s.Equal(http.StatusOK, rec.Code) {\n\t\t\t\tvar emails []*dto.EmailResponse\n\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &emails))\n\t\t\t\ts.Equal(currentTest.expectedCount, len(emails))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *emailSuite) TestEmailHandler_Create() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tauditLogRecordsKey := \"email_created\"\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\ttests := []struct {\n\t\tname                 string\n\t\temail                string\n\t\tuserId               uuid.UUID\n\t\tmaxNumberOfAddresses int\n\t\trequiresVerification bool\n\t\texpectedStatusCode   int\n\t\tupsertsRecords       bool\n\t\texpectedEmailUserId  uuid.UUID\n\t}{\n\t\t{\n\t\t\tname:                 \"should reject the request when the user has already reached the maximum number of email addresses\",\n\t\t\temail:                \"rejection1@example.com\",\n\t\t\tuserId:               uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\"),\n\t\t\tmaxNumberOfAddresses: 0,\n\t\t\trequiresVerification: false,\n\t\t\texpectedStatusCode:   409,\n\t\t\tupsertsRecords:       false,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should error if email address is already in use\",\n\t\t\temail:                \"john.doe@example.com\",\n\t\t\tuserId:               uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t\trequiresVerification: false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   400,\n\t\t\tupsertsRecords:       false,\n\t\t\texpectedEmailUserId:  uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\"),\n\t\t},\n\t\t{\n\t\t\tname:                 \"should assign the email address to the user if not yet assigned and does not require verification\",\n\t\t\temail:                \"john.doe+6@example.com\",\n\t\t\tuserId:               uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t\trequiresVerification: false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   200,\n\t\t\tupsertsRecords:       true,\n\t\t\texpectedEmailUserId:  uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t},\n\t\t{\n\t\t\tname:                 \"should not assign the email address to the user if not yet assigned and requires verification\",\n\t\t\temail:                \"john.doe+7@example.com\",\n\t\t\tuserId:               uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t\trequiresVerification: true,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   200,\n\t\t\tupsertsRecords:       false,\n\t\t\texpectedEmailUserId:  uuid.Nil,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should create email record with nil user ID if verification required\",\n\t\t\temail:                \"test.email.verification@example.com\",\n\t\t\tuserId:               uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t\trequiresVerification: true,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   200,\n\t\t\tupsertsRecords:       true,\n\t\t\texpectedEmailUserId:  uuid.Nil,\n\t\t},\n\t\t{\n\t\t\tname:                 \"should create email record with user ID if verification not required\",\n\t\t\temail:                \"test.email.noverification@example.com\",\n\t\t\tuserId:               uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t\trequiresVerification: false,\n\t\t\tmaxNumberOfAddresses: 100,\n\t\t\texpectedStatusCode:   200,\n\t\t\tupsertsRecords:       true,\n\t\t\texpectedEmailUserId:  uuid.FromStringOrNil(\"d41df4b7-c055-45e6-9faf-61aa92a4032e\"),\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\tcfg := test.DefaultConfig\n\t\t\tcfg.AuditLog.Storage.Enabled = true\n\t\t\tcfg.Email.RequireVerification = currentTest.requiresVerification\n\t\t\tcfg.Email.Limit = currentTest.maxNumberOfAddresses\n\t\t\te := NewPublicRouter(&cfg, s.Storage, nil, nil)\n\t\t\tcookie, err := generateSessionCookie(s.Storage, currentTest.userId)\n\t\t\ts.Require().NoError(err)\n\n\t\t\tbody := dto.EmailCreateRequest{\n\t\t\t\tAddress: currentTest.email,\n\t\t\t}\n\t\t\tbodyJson, err := json.Marshal(body)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, \"/emails\", bytes.NewReader(bodyJson))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\treq.AddCookie(cookie)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\tauditLogsCountBefore := s.getAuditLogRecordsCount(auditLogRecordsKey)\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tauditLogsCountAfter := s.getAuditLogRecordsCount(auditLogRecordsKey)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(currentTest.email)\n\t\t\ts.Require().NoError(err)\n\n\t\t\tif currentTest.upsertsRecords {\n\t\t\t\ts.NotNil(email)\n\t\t\t}\n\n\t\t\tif email != nil {\n\t\t\t\tif currentTest.expectedEmailUserId != uuid.Nil {\n\t\t\t\t\ts.Equal(currentTest.expectedEmailUserId, *email.UserID)\n\t\t\t\t} else {\n\t\t\t\t\ts.Nil(email.UserID)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif rec.Code == http.StatusOK {\n\t\t\t\ts.Equal(auditLogsCountBefore+1, auditLogsCountAfter)\n\t\t\t} else {\n\t\t\t\ts.Equal(auditLogsCountBefore, auditLogsCountAfter)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *emailSuite) TestEmailHandler_SetPrimaryEmail() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\toldPrimaryEmailId := uuid.FromStringOrNil(\"51b7c175-ceb6-45ba-aae6-0092221c1b84\")\n\tnewPrimaryEmailId := uuid.FromStringOrNil(\"8bb4c8a7-a3e6-48bb-b54f-20e3b485ab33\")\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, fmt.Sprintf(\"/emails/%s/set_primary\", newPrimaryEmailId), nil)\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\tif s.Equal(http.StatusNoContent, rec.Code) {\n\t\temails, err := s.Storage.GetEmailPersister().FindByUserId(userId)\n\t\ts.Require().NoError(err)\n\n\t\ts.Equal(3, len(emails))\n\t\tfor _, email := range emails {\n\t\t\tif email.ID == newPrimaryEmailId {\n\t\t\t\ts.True(email.IsPrimary())\n\t\t\t} else if email.ID == oldPrimaryEmailId {\n\t\t\t\ts.False(email.IsPrimary())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (s *emailSuite) TestEmailHandler_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/email\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\ttests := []struct {\n\t\tname          string\n\t\temailId       uuid.UUID\n\t\tresponseCode  int\n\t\texpectedCount int\n\t}{\n\t\t{\n\t\t\tname:          \"should delete the email address\",\n\t\t\temailId:       uuid.FromStringOrNil(\"8bb4c8a7-a3e6-48bb-b54f-20e3b485ab33\"),\n\t\t\tresponseCode:  http.StatusNoContent,\n\t\t\texpectedCount: 2,\n\t\t},\n\t\t{\n\t\t\tname:          \"should not delete the primary email address\",\n\t\t\temailId:       uuid.FromStringOrNil(\"51b7c175-ceb6-45ba-aae6-0092221c1b84\"),\n\t\t\tresponseCode:  http.StatusConflict,\n\t\t\texpectedCount: 2,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/emails/%s\", currentTest.emailId), nil)\n\t\t\treq.AddCookie(cookie)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\t\t\tif s.Equal(currentTest.responseCode, rec.Code) {\n\t\t\t\temails, err := s.Storage.GetEmailPersister().FindByUserId(userId)\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Equal(currentTest.expectedCount, len(emails))\n\t\t\t}\n\t\t})\n\t}\n\n}\n\nfunc (s *emailSuite) getAuditLogRecordsCount(code string) int {\n\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{code}, \"\", \"\", \"\", \"\")\n\ts.Require().NoError(lerr)\n\treturn len(logs)\n}\n"
  },
  {
    "path": "backend/handler/health.go",
    "content": "package handler\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"net/http\"\n)\n\ntype HealthHandler struct{}\n\nfunc NewHealthHandler() *HealthHandler {\n\treturn &HealthHandler{}\n}\n\nfunc (handler *HealthHandler) Ready(c echo.Context) error {\n\treturn c.JSON(http.StatusOK, map[string]bool{\"ready\": true})\n}\n\nfunc (handler *HealthHandler) Alive(c echo.Context) error {\n\treturn c.JSON(http.StatusOK, map[string]bool{\"alive\": true})\n}\n"
  },
  {
    "path": "backend/handler/health_test.go",
    "content": "package handler\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestHealthHandler_Ready(t *testing.T) {\n\te := echo.New()\n\treq := httptest.NewRequest(http.MethodGet, \"/health/ready\", nil)\n\trec := httptest.NewRecorder()\n\tc := e.NewContext(req, rec)\n\th := NewHealthHandler()\n\n\tif assert.NoError(t, h.Ready(c)) {\n\t\tassert.Equal(t, `{\"ready\":true}`, strings.TrimSuffix(rec.Body.String(), \"\\n\"))\n\t}\n}\n\nfunc TestHealthHandler_Alive(t *testing.T) {\n\te := echo.New()\n\treq := httptest.NewRequest(http.MethodGet, \"/health/alive\", nil)\n\trec := httptest.NewRecorder()\n\tc := e.NewContext(req, rec)\n\th := NewHealthHandler()\n\n\tif assert.NoError(t, h.Alive(c)) {\n\t\tassert.Equal(t, `{\"alive\":true}`, strings.TrimSuffix(rec.Body.String(), \"\\n\"))\n\t}\n}\n"
  },
  {
    "path": "backend/handler/helpers_test.go",
    "content": "package handler\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk/local_db\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n)\n\nfunc getDefaultSessionManager(storage persistence.Persister) session.Manager {\n\tjwkManager, _ := local_db.NewDefaultManager(test.DefaultConfig.Secrets.Keys, storage.GetJwkPersister())\n\tsessionManager, _ := session.NewManager(jwkManager, test.DefaultConfig)\n\treturn sessionManager\n}\n\nfunc generateSessionCookie(storage persistence.Persister, userId uuid.UUID) (*http.Cookie, error) {\n\tmanager := getDefaultSessionManager(storage)\n\ttoken, rawToken, err := manager.GenerateJWT(dto.UserJWT{\n\t\tUserID: userId.String(),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsessionID, _ := rawToken.Get(\"session_id\")\n\t_ = storage.GetSessionPersister().Create(models.Session{\n\t\tID:        uuid.FromStringOrNil(sessionID.(string)),\n\t\tUserID:    userId,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t\tExpiresAt: nil,\n\t\tLastUsed:  time.Now(),\n\t})\n\tcookie, err := manager.GenerateCookie(token)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cookie, nil\n}\n"
  },
  {
    "path": "backend/handler/metadata_admin.go",
    "content": "package handler\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\n\tjsonpatch \"github.com/evanphx/json-patch/v5\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/tidwall/gjson\"\n)\n\ntype MetadataAdminHandler struct {\n\tpersister persistence.Persister\n}\n\nfunc NewMetadataAdminHandler(persister persistence.Persister) *MetadataAdminHandler {\n\treturn &MetadataAdminHandler{\n\t\tpersister: persister,\n\t}\n}\n\nfunc (h *MetadataAdminHandler) GetMetadata(c echo.Context) error {\n\tuserID, err := uuid.FromString(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"invalid user id\")\n\t}\n\n\tuserExists, err := h.persister.GetConnection().Where(\"id = ?\", userID).Exists(&models.User{ID: userID})\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, \"could not fetch user\").SetInternal(err)\n\t}\n\n\tif !userExists {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\").SetInternal(err)\n\t}\n\n\tmetadataModel, err := h.persister.GetUserMetadataPersister().Get(userID)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, \"could not fetch metadata\").SetInternal(err)\n\t}\n\n\tresponse := admin.NewMetadata(metadataModel)\n\n\tif response == nil {\n\t\treturn c.NoContent(http.StatusNoContent)\n\t}\n\treturn c.JSON(http.StatusOK, response)\n}\n\nfunc (h *MetadataAdminHandler) PatchMetadata(c echo.Context) error {\n\tuserID, err := uuid.FromString(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"invalid user id\")\n\t}\n\n\tpatchMetadataRequest, err := loadDto[admin.PatchMetadataRequest](c)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserExists, err := h.persister.GetConnection().Where(\"id = ?\", userID).Exists(&models.User{ID: userID})\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, \"could not fetch user\").SetInternal(err)\n\t}\n\n\tif !userExists {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\").SetInternal(err)\n\t}\n\n\tcurrentMetadataModel, err := h.persister.GetUserMetadataPersister().Get(userID)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, \"could not fetch metadata\").SetInternal(err)\n\t}\n\n\t_, err = h.applyMetadataPatch(currentMetadataModel, patchMetadataRequest)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, \"could not patch metadata\").SetInternal(err)\n\t}\n\n\terr = h.persister.GetUserMetadataPersister().Update(currentMetadataModel)\n\tif err != nil {\n\t\tif persistence.IsMetadataLimitExceededError(err) {\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, err.Error()).\n\t\t\t\tSetInternal(errors.Unwrap(err))\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, \"could not save metadata\").\n\t\t\tSetInternal(err)\n\t}\n\n\tresponse := admin.NewMetadata(currentMetadataModel)\n\n\tif response == nil {\n\t\treturn c.NoContent(http.StatusNoContent)\n\t}\n\treturn c.JSON(http.StatusOK, response)\n}\n\nfunc (h *MetadataAdminHandler) applyMetadataPatch(currentMetadataModel *models.UserMetadata, patchMetadataRequest *admin.PatchMetadataRequest) ([]byte, error) {\n\tif patchMetadataRequest.Metadata.Raw == \"null\" {\n\t\tcurrentMetadataModel.Public = nulls.String{}\n\t\tcurrentMetadataModel.Private = nulls.String{}\n\t\tcurrentMetadataModel.Unsafe = nulls.String{}\n\t\treturn []byte(\"{}\"), nil\n\t}\n\n\tcurrentMetadataJSON, err := h.buildCurrentMetadataJSON(currentMetadataModel)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpatchedMetadataBytes, err := jsonpatch.MergePatch(\n\t\t[]byte(currentMetadataJSON),\n\t\t[]byte(patchMetadataRequest.Metadata.String()),\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not apply merge patch: %w\", err)\n\t}\n\n\tif !json.Valid(patchedMetadataBytes) {\n\t\treturn nil, fmt.Errorf(\"invalid metadata JSON after applying merge patch\")\n\t}\n\n\tif err = h.updateMetadataModel(currentMetadataModel, patchedMetadataBytes); err != nil {\n\t\treturn nil, fmt.Errorf(\"could not update user metadata model: %w\", err)\n\t}\n\n\treturn patchedMetadataBytes, nil\n}\n\nfunc (h *MetadataAdminHandler) buildCurrentMetadataJSON(metadata *models.UserMetadata) (string, error) {\n\tresult := make(map[string]json.RawMessage)\n\n\tif metadata.Public.Valid && metadata.Public.String != \"{}\" {\n\t\tresult[\"public_metadata\"] = json.RawMessage(metadata.Public.String)\n\t}\n\n\tif metadata.Private.Valid && metadata.Private.String != \"{}\" {\n\t\tresult[\"private_metadata\"] = json.RawMessage(metadata.Private.String)\n\t}\n\n\tif metadata.Unsafe.Valid && metadata.Unsafe.String != \"{}\" {\n\t\tresult[\"unsafe_metadata\"] = json.RawMessage(metadata.Unsafe.String)\n\t}\n\n\tif len(result) == 0 {\n\t\treturn \"{}\", nil\n\t}\n\n\tbytes, err := json.Marshal(result)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not build JSON for current metadata: %w\", err)\n\t}\n\n\treturn string(bytes), nil\n}\n\nfunc (h *MetadataAdminHandler) updateMetadataModel(metadata *models.UserMetadata, patchedBytes []byte) error {\n\n\tmetadataTypes := map[string]*nulls.String{\n\t\t\"public_metadata\":  &metadata.Public,\n\t\t\"private_metadata\": &metadata.Private,\n\t\t\"unsafe_metadata\":  &metadata.Unsafe,\n\t}\n\n\tfor key, target := range metadataTypes {\n\t\tif gjson.GetBytes(patchedBytes, key).Exists() {\n\t\t\tvalue := gjson.GetBytes(patchedBytes, key).String()\n\t\t\tif !json.Valid([]byte(value)) {\n\t\t\t\treturn fmt.Errorf(\"invalid JSON for %s\", key)\n\t\t\t}\n\t\t\t*target = nulls.String{\n\t\t\t\tValid:  true,\n\t\t\t\tString: value,\n\t\t\t}\n\t\t} else {\n\t\t\t*target = nulls.String{}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/handler/metadata_admin_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/tidwall/gjson\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n)\n\nfunc TestMetadataAdminSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(metadataAdminSuite))\n}\n\ntype metadataAdminSuite struct {\n\ttest.Suite\n}\n\nfunc (s *metadataAdminSuite) TestMetadataAdminHandler_Get() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\texpectedStatusCode int\n\t\texpectedMetadata   *admin.Metadata\n\t}{\n\t\t{\n\t\t\tname:               \"should return metadata for user with metadata\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedMetadata: &admin.Metadata{\n\t\t\t\tPublic: json.RawMessage(`{\n\t\t\t\t\t\"existing_public_str\": \"data\",\n\t\t\t\t\t\"existing_public_num\": 1,\n\t\t\t\t\t\"existing_public_bool\": true\n\t\t\t\t}`),\n\t\t\t\tPrivate: json.RawMessage(`{\n\t\t\t\t\t\"existing_private_str\": \"data\",\n\t\t\t\t\t\"existing_private_arr\": [\n\t\t\t\t\t\t\"existing_private_arr_0\",\n\t\t\t\t\t\t\"existing_private_arr_1\"\n\t\t\t\t\t]\n\t\t\t\t}`),\n\t\t\t\tUnsafe: json.RawMessage(`{\n\t\t\t\t\t\"existing_unsafe_str\": \"data\",\n\t\t\t\t\t\"existing_unsafe_obj\": {\n\t\t\t\t\t\t\"existing_unsafe_obj_key\": \"existing_unsafe_obj_value\"\n\t\t\t\t\t}\n\t\t\t\t}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:               \"should return no content for user without metadata\",\n\t\t\tuserId:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/metadata\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\treq := httptest.NewRequest(\n\t\t\t\thttp.MethodGet,\n\t\t\t\tfmt.Sprintf(\"/users/%s/metadata\", currentTest.userId),\n\t\t\t\tnil,\n\t\t\t)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif currentTest.expectedStatusCode == http.StatusOK {\n\t\t\t\tvar metadataResponse *admin.Metadata\n\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &metadataResponse))\n\n\t\t\t\tif currentTest.expectedMetadata == nil {\n\t\t\t\t\ts.Nil(metadataResponse)\n\t\t\t\t} else {\n\t\t\t\t\ts.NotNil(metadataResponse)\n\n\t\t\t\t\ts.Require().Equal(\n\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Public, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t\t\t\tgjson.GetBytes(metadataResponse.Public, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t\t\t)\n\t\t\t\t\ts.Require().Equal(\n\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Private, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t\t\t\tgjson.GetBytes(metadataResponse.Private, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t\t\t)\n\t\t\t\t\ts.Require().Equal(\n\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Unsafe, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t\t\t\tgjson.GetBytes(metadataResponse.Unsafe, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\nfunc (s *metadataAdminSuite) TestMetadataAdminHandler_Patch_Errors() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\tpatchMetadata      json.RawMessage\n\t\texpectedStatusCode int\n\t\texpectedMetadata   *admin.Metadata\n\t}{\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\tpatchMetadata:      json.RawMessage(`{\"public_metadata\":{\"key\":\"value\"}}`),\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\tpatchMetadata:      json.RawMessage(`{\"public_metadata\":{\"key\":\"value\"}}`),\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\tpatchMetadata:      json.RawMessage(`{\"public_metadata\":{\"key\":\"value\"}}`),\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on invalid JSON\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata:      json.RawMessage(`{\"public_metadata\":\"key\":\"value\"`),\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on top level empty string as metadata patch\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata:      json.RawMessage(`\"\"`),\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on top level string as metadata patch\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata:      json.RawMessage(`\"invalid\"`),\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on top level array as metadata patch\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata:      json.RawMessage(`[\"in\", \"valid\"]`),\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on exceeding metadata size limit (3000)\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata:      json.RawMessage(metadataExceedingLimit),\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/metadata\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\treq := httptest.NewRequest(\n\t\t\t\thttp.MethodPatch,\n\t\t\t\tfmt.Sprintf(\"/users/%s/metadata\", currentTest.userId),\n\t\t\t\tbytes.NewReader(currentTest.patchMetadata),\n\t\t\t)\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t})\n\t}\n}\n\nfunc (s *metadataAdminSuite) TestMetadataAdminHandler_Patch() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\tpatchMetadata      json.RawMessage\n\t\texpectedStatusCode int\n\t\texpectedMetadata   *admin.Metadata\n\t}{\n\t\t{\n\t\t\tname:               \"should do nothing on empty patch object\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata:      json.RawMessage(`{}`),\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedMetadata: &admin.Metadata{\n\t\t\t\tPublic: json.RawMessage(`{\n\t\t\t\t\t\"existing_public_str\": \"data\",\n\t\t\t\t\t\"existing_public_num\": 1,\n\t\t\t\t\t\"existing_public_bool\": true\n\t\t\t\t}`),\n\t\t\t\tPrivate: json.RawMessage(`{\n\t\t\t\t\t\"existing_private_str\": \"data\",\n\t\t\t\t\t\"existing_private_arr\": [\n\t\t\t\t\t\t\"existing_private_arr_0\",\n\t\t\t\t\t\t\"existing_private_arr_1\"\n\t\t\t\t\t]\n\t\t\t\t}`),\n\t\t\t\tUnsafe: json.RawMessage(`{\n\t\t\t\t\t\"existing_unsafe_str\": \"data\",\n\t\t\t\t\t\"existing_unsafe_obj\": {\n\t\t\t\t\t\t\"existing_unsafe_obj_key\": \"existing_unsafe_obj_value\"\n\t\t\t\t\t}\n\t\t\t\t}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"should do nothing on empty patch object for top level keys\",\n\t\t\tuserId: \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata: json.RawMessage(`{\n\t\t\t\t\"public_metadata\": {},\n\t\t\t\t\"private_metadata\": {},\n\t\t\t\t\"unsafe_metadata\": {}\n\t\t\t}`),\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedMetadata: &admin.Metadata{\n\t\t\t\tPublic: json.RawMessage(`{\n\t\t\t\t\t\"existing_public_str\": \"data\",\n\t\t\t\t\t\"existing_public_num\": 1,\n\t\t\t\t\t\"existing_public_bool\": true\n\t\t\t\t}`),\n\t\t\t\tPrivate: json.RawMessage(`{\n\t\t\t\t\t\"existing_private_str\": \"data\",\n\t\t\t\t\t\"existing_private_arr\": [\n\t\t\t\t\t\t\"existing_private_arr_0\",\n\t\t\t\t\t\t\"existing_private_arr_1\"\n\t\t\t\t\t]\n\t\t\t\t}`),\n\t\t\t\tUnsafe: json.RawMessage(`{\n\t\t\t\t\t\"existing_unsafe_str\": \"data\",\n\t\t\t\t\t\"existing_unsafe_obj\": {\n\t\t\t\t\t\t\"existing_unsafe_obj_key\": \"existing_unsafe_obj_value\"\n\t\t\t\t\t}\n\t\t\t\t}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"should ignore unknown top level keys\",\n\t\t\tuserId: \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata: json.RawMessage(`{\n\t\t\t\t\"public_metadata\": {\n\t\t\t\t\t\"new_key\":\"new_value\"\n\t\t\t\t},\n\t\t\t\t\"unknown_metadata\": {\n\t\t\t\t\t\"key\":\"value\"\n\t\t\t\t}\n\t\t\t}`),\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedMetadata: &admin.Metadata{\n\t\t\t\tPublic: json.RawMessage(`{\n\t\t\t\t\t\"existing_public_str\": \"data\",\n\t\t\t\t\t\"existing_public_num\": 1,\n\t\t\t\t\t\"existing_public_bool\": true,\n\t\t\t\t\t\"new_key\": \"new_value\"\n\t\t\t\t}`),\n\t\t\t\tPrivate: json.RawMessage(`{\n\t\t\t\t\t\"existing_private_str\": \"data\",\n\t\t\t\t\t\"existing_private_arr\": [\n\t\t\t\t\t\t\"existing_private_arr_0\",\n\t\t\t\t\t\t\"existing_private_arr_1\"\n\t\t\t\t\t]\n\t\t\t\t}`),\n\t\t\t\tUnsafe: json.RawMessage(`{\n\t\t\t\t\t\"existing_unsafe_str\": \"data\",\n\t\t\t\t\t\"existing_unsafe_obj\": {\n\t\t\t\t\t\t\"existing_unsafe_obj_key\": \"existing_unsafe_obj_value\"\n\t\t\t\t\t}\n\t\t\t\t}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"should merge new fields with existing metadata\",\n\t\t\tuserId: \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata: json.RawMessage(`{\n\t\t\t\t\"public_metadata\": {\n\t\t\t\t\t\"existing_public_str\": \"data_updated\",\n\t\t\t\t\t\"new_key\": \"new_value\"\n\t\t\t\t},\n\t\t\t\t\"private_metadata\": {\n\t\t\t\t\t\"existing_private_arr\": [\n\t\t\t\t\t\t\"existing_private_arr_0_updated\"\n\t\t\t\t\t],\n\t\t\t\t\t\"new_key\": \"new_value\"\n\t\t\t\t},\n\t\t\t\t\"unsafe_metadata\": {\n\t\t\t\t\t\"existing_unsafe_obj\": {\n\t\t\t\t\t\t\"existing_unsafe_obj_key\": \"existing_unsafe_obj_value_updated\"\n\t\t\t\t\t},\n\t\t\t\t\t\"new_key\": \"new_value\"\n\t\t\t\t}\n\t\t\t}`),\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedMetadata: &admin.Metadata{\n\t\t\t\tPublic: json.RawMessage(`{\n\t\t\t\t\t\"existing_public_str\": \"data_updated\",\n\t\t\t\t\t\"existing_public_num\": 1,\n\t\t\t\t\t\"existing_public_bool\": true,\n\t\t\t\t\t\"new_key\": \"new_value\"\n\t\t\t\t}`),\n\t\t\t\tPrivate: json.RawMessage(`{\n\t\t\t\t\t\"existing_private_str\": \"data\",\n\t\t\t\t\t\"existing_private_arr\": [\n\t\t\t\t\t\t\"existing_private_arr_0_updated\"\n\t\t\t\t\t],\n\t\t\t\t\t\"new_key\": \"new_value\"\n\t\t\t\t}`),\n\t\t\t\tUnsafe: json.RawMessage(`{\n\t\t\t\t\t\"existing_unsafe_str\": \"data\",\n\t\t\t\t\t\"existing_unsafe_obj\": {\n\t\t\t\t\t\t\"existing_unsafe_obj_key\": \"existing_unsafe_obj_value_updated\"\n\t\t\t\t\t},\n\t\t\t\t\t\"new_key\": \"new_value\"\n\t\t\t\t}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"should clear specific metadata fields when sending null\",\n\t\t\tuserId: \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata: json.RawMessage(`{\n\t\t\t\t\"public_metadata\": null,\n\t\t\t\t\"private_metadata\": {\"existing_private_str\": null},\n\t\t\t\t\"unsafe_metadata\": {\"existing_unsafe_str\": null}\n\t\t\t}`),\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedMetadata: &admin.Metadata{\n\t\t\t\tPrivate: json.RawMessage(`{\n\t\t\t\t\t\"existing_private_arr\": [\n\t\t\t\t\t\t\"existing_private_arr_0\",\n\t\t\t\t\t\t\"existing_private_arr_1\"\n\t\t\t\t\t]\n\t\t\t\t}`),\n\t\t\t\tUnsafe: json.RawMessage(`{\n\t\t\t\t\t\"existing_unsafe_obj\": {\n\t\t\t\t\t\t\"existing_unsafe_obj_key\": \"existing_unsafe_obj_value\"\n\t\t\t\t\t}\n\t\t\t\t}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"should clear all metadata fields when sending null for all fields\",\n\t\t\tuserId: \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpatchMetadata: json.RawMessage(`{\n\t\t\t\t\"public_metadata\": null,\n\t\t\t\t\"private_metadata\": null,\n\t\t\t\t\"unsafe_metadata\": null\n\t\t\t}`),\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"should create metadata for user without metadata\",\n\t\t\tuserId: \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\tpatchMetadata: json.RawMessage(`{\n\t\t\t\t\"public_metadata\": {\"public_key\": \"public_value\"},\n\t\t\t\t\"private_metadata\": {\"private_key\": \"private_value\"},\n\t\t\t\t\"unsafe_metadata\": {\"unsafe_key\": \"unsafe_value\"}\n\t\t\t}`),\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedMetadata: &admin.Metadata{\n\t\t\t\tPublic:  json.RawMessage(`{\"public_key\":\"public_value\"}`),\n\t\t\t\tPrivate: json.RawMessage(`{\"private_key\":\"private_value\"}`),\n\t\t\t\tUnsafe:  json.RawMessage(`{\"unsafe_key\":\"unsafe_value\"}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:               \"should clear all metadata when sending null\",\n\t\t\tuserId:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\tpatchMetadata:      json.RawMessage(`null`),\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t\texpectedMetadata:   nil,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/metadata\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\treq := httptest.NewRequest(\n\t\t\t\thttp.MethodPatch,\n\t\t\t\tfmt.Sprintf(\"/users/%s/metadata\", currentTest.userId),\n\t\t\t\tbytes.NewReader(currentTest.patchMetadata),\n\t\t\t)\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif currentTest.expectedStatusCode == http.StatusOK ||\n\t\t\t\tcurrentTest.expectedStatusCode == http.StatusNoContent {\n\n\t\t\t\t// Verify the response contains correct updated metadata\n\t\t\t\tvar metadataResponse *admin.Metadata\n\t\t\t\tif rec.Code == http.StatusOK {\n\t\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &metadataResponse))\n\t\t\t\t} else if rec.Code == http.StatusNoContent {\n\t\t\t\t\ts.Nil(rec.Body.Bytes())\n\t\t\t\t}\n\t\t\t\tif currentTest.expectedMetadata == nil {\n\t\t\t\t\ts.Nil(metadataResponse)\n\t\t\t\t} else {\n\t\t\t\t\ts.NotNil(metadataResponse)\n\t\t\t\t\ts.Equal(\n\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Public, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\tgjson.GetBytes(metadataResponse.Public, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\tString(),\n\t\t\t\t\t)\n\t\t\t\t\ts.Require().Equal(\n\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Private, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\tgjson.GetBytes(metadataResponse.Private, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\tString(),\n\t\t\t\t\t)\n\t\t\t\t\ts.Require().Equal(\n\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Unsafe, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\tgjson.GetBytes(metadataResponse.Unsafe, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\tString(),\n\t\t\t\t\t)\n\n\t\t\t\t\t// Also verify the metadata was actually updated in the database\n\t\t\t\t\tuserID, err := uuid.FromString(currentTest.userId)\n\t\t\t\t\ts.Require().NoError(err)\n\t\t\t\t\tmetadataModel, err := s.Storage.GetUserMetadataPersister().Get(userID)\n\t\t\t\t\ts.Require().NoError(err)\n\t\t\t\t\tif currentTest.expectedMetadata == nil {\n\t\t\t\t\t\ts.False(metadataModel.Public.Valid)\n\t\t\t\t\t\ts.False(metadataModel.Private.Valid)\n\t\t\t\t\t\ts.False(metadataModel.Unsafe.Valid)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif currentTest.expectedMetadata.Public != nil {\n\t\t\t\t\t\t\ts.True(metadataModel.Public.Valid)\n\t\t\t\t\t\t\ts.Equal(\n\t\t\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Public, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\t\t\tgjson.GetBytes([]byte(metadataModel.Public.String), `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ts.False(metadataModel.Public.Valid)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif currentTest.expectedMetadata.Private != nil {\n\t\t\t\t\t\t\ts.True(metadataModel.Private.Valid)\n\t\t\t\t\t\t\ts.Equal(\n\t\t\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Private, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\t\t\tgjson.GetBytes([]byte(metadataModel.Private.String), `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ts.False(metadataModel.Private.Valid)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif currentTest.expectedMetadata.Unsafe != nil {\n\t\t\t\t\t\t\ts.True(metadataModel.Unsafe.Valid)\n\t\t\t\t\t\t\ts.Equal(\n\t\t\t\t\t\t\t\tgjson.GetBytes(currentTest.expectedMetadata.Unsafe, `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\t\t\tgjson.GetBytes([]byte(metadataModel.Unsafe.String), `@pretty:{\"sortKeys\":true}`).\n\t\t\t\t\t\t\t\t\tString(),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ts.False(metadataModel.Unsafe.Valid)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nconst metadataExceedingLimit = `{\n  \"public_metadata\": {\n    \"users\": [\n      {\n        \"id\": 1,\n        \"name\": \"Alice\",\n        \"email\": \"alice@example.com\",\n        \"active\": true,\n        \"roles\": [\n          \"admin\",\n          \"editor\"\n        ],\n        \"settings\": {\n          \"theme\": \"dark\",\n          \"language\": \"en\"\n        },\n        \"preferences\": {\n          \"newsletter\": true,\n          \"notifications\": {\n            \"email\": true,\n            \"sms\": false\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-05-07T10:00:00Z\",\n            \"action\": \"login\"\n          },\n          {\n            \"timestamp\": \"2025-05-06T08:32:00Z\",\n            \"action\": \"update_profile\"\n          }\n        ]\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Bob\",\n        \"email\": \"bob@example.com\",\n        \"active\": true,\n        \"roles\": [\n          \"user\"\n        ],\n        \"settings\": {\n          \"theme\": \"light\",\n          \"language\": \"fr\"\n        },\n        \"preferences\": {\n          \"newsletter\": false,\n          \"notifications\": {\n            \"email\": true,\n            \"sms\": true\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-05-06T22:00:00Z\",\n            \"action\": \"logout\"\n          },\n          {\n            \"timestamp\": \"2025-05-05T14:12:00Z\",\n            \"action\": \"purchase\"\n          }\n        ]\n      },\n      {\n        \"id\": 3,\n        \"name\": \"Carol\",\n        \"email\": \"carol@example.com\",\n        \"active\": false,\n        \"roles\": [\n          \"user\"\n        ],\n        \"settings\": {\n          \"theme\": \"dark\",\n          \"language\": \"es\"\n        },\n        \"preferences\": {\n          \"newsletter\": true,\n          \"notifications\": {\n            \"email\": false,\n            \"sms\": false\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-05-04T19:40:00Z\",\n            \"action\": \"deactivate_account\"\n          }\n        ]\n      },\n      {\n        \"id\": 4,\n        \"name\": \"Dan\",\n        \"email\": \"dan@example.com\",\n        \"active\": true,\n        \"roles\": [\n          \"user\",\n          \"moderator\"\n        ],\n        \"settings\": {\n          \"theme\": \"light\",\n          \"language\": \"de\"\n        },\n        \"preferences\": {\n          \"newsletter\": true,\n          \"notifications\": {\n            \"email\": true,\n            \"sms\": false\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-05-03T08:00:00Z\",\n            \"action\": \"flag_post\"\n          }\n        ]\n      },\n      {\n        \"id\": 5,\n        \"name\": \"Eve\",\n        \"email\": \"eve@example.com\",\n        \"active\": true,\n        \"roles\": [\n          \"editor\"\n        ],\n        \"settings\": {\n          \"theme\": \"dark\",\n          \"language\": \"it\"\n        },\n        \"preferences\": {\n          \"newsletter\": false,\n          \"notifications\": {\n            \"email\": false,\n            \"sms\": true\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-05-01T13:20:00Z\",\n            \"action\": \"edit_post\"\n          },\n          {\n            \"timestamp\": \"2025-04-30T09:45:00Z\",\n            \"action\": \"comment\"\n          }\n        ]\n      },\n      {\n        \"id\": 6,\n        \"name\": \"Frank\",\n        \"email\": \"frank@example.com\",\n        \"active\": false,\n        \"roles\": [\n          \"user\"\n        ],\n        \"settings\": {\n          \"theme\": \"light\",\n          \"language\": \"en\"\n        },\n        \"preferences\": {\n          \"newsletter\": true,\n          \"notifications\": {\n            \"email\": true,\n            \"sms\": false\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-04-29T10:00:00Z\",\n            \"action\": \"unsubscribe\"\n          }\n        ]\n      },\n      {\n        \"id\": 7,\n        \"name\": \"Grace\",\n        \"email\": \"grace@example.com\",\n        \"active\": true,\n        \"roles\": [\n          \"admin\"\n        ],\n        \"settings\": {\n          \"theme\": \"dark\",\n          \"language\": \"en\"\n        },\n        \"preferences\": {\n          \"newsletter\": false,\n          \"notifications\": {\n            \"email\": false,\n            \"sms\": false\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-04-28T11:30:00Z\",\n            \"action\": \"ban_user\"\n          }\n        ]\n      },\n      {\n        \"id\": 8,\n        \"name\": \"Hank\",\n        \"email\": \"hank@example.com\",\n        \"active\": true,\n        \"roles\": [\n          \"user\"\n        ],\n        \"settings\": {\n          \"theme\": \"light\",\n          \"language\": \"pt\"\n        },\n        \"preferences\": {\n          \"newsletter\": true,\n          \"notifications\": {\n            \"email\": true,\n            \"sms\": true\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-04-27T07:00:00Z\",\n            \"action\": \"like_post\"\n          }\n        ]\n      },\n      {\n        \"id\": 9,\n        \"name\": \"Ivy\",\n        \"email\": \"ivy@example.com\",\n        \"active\": false,\n        \"roles\": [\n          \"editor\"\n        ],\n        \"settings\": {\n          \"theme\": \"dark\",\n          \"language\": \"ja\"\n        },\n        \"preferences\": {\n          \"newsletter\": false,\n          \"notifications\": {\n            \"email\": true,\n            \"sms\": false\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-04-26T06:00:00Z\",\n            \"action\": \"edit_post\"\n          }\n        ]\n      },\n      {\n        \"id\": 10,\n        \"name\": \"Jack\",\n        \"email\": \"jack@example.com\",\n        \"active\": true,\n        \"roles\": [\n          \"user\"\n        ],\n        \"settings\": {\n          \"theme\": \"light\",\n          \"language\": \"ru\"\n        },\n        \"preferences\": {\n          \"newsletter\": true,\n          \"notifications\": {\n            \"email\": false,\n            \"sms\": true\n          }\n        },\n        \"activity\": [\n          {\n            \"timestamp\": \"2025-04-25T15:00:00Z\",\n            \"action\": \"create_post\"\n          }\n        ]\n      }\n    ]\n  }\n}`\n"
  },
  {
    "path": "backend/handler/otp_admin.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"net/http\"\n)\n\ntype OTPAdminHandler interface {\n\tGet(ctx echo.Context) error\n\tDelete(ctx echo.Context) error\n}\n\ntype otpAdminHandler struct {\n\tpersister persistence.Persister\n}\n\nfunc NewOTPAdminHandler(persister persistence.Persister) OTPAdminHandler {\n\treturn &otpAdminHandler{persister: persister}\n}\n\nfunc (h *otpAdminHandler) Get(ctx echo.Context) error {\n\tgetDto, err := loadDto[admin.GetOTPRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(getDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuserModel, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif userModel == nil || userModel.OTPSecret == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\treturn ctx.JSON(http.StatusOK, admin.OTPDto{\n\t\tID:        userModel.OTPSecret.ID,\n\t\tCreatedAt: userModel.OTPSecret.CreatedAt,\n\t})\n}\n\nfunc (h *otpAdminHandler) Delete(ctx echo.Context) error {\n\tdeleteDto, err := loadDto[admin.GetOTPRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(deleteDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuserModel, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif userModel == nil || userModel.OTPSecret == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\terr = h.persister.GetOTPSecretPersister().Delete(userModel.OTPSecret)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.NoContent(http.StatusNoContent)\n}\n"
  },
  {
    "path": "backend/handler/otp_admin_test.go",
    "content": "package handler\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestOtpAdminSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(otpAdminSuite))\n}\n\ntype otpAdminSuite struct {\n\ttest.Suite\n}\n\nfunc (s *otpAdminSuite) TestOtpAdminHandler_Get() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/otp\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should return otp credential\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if user has no otp credential\",\n\t\t\tuserId:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s/otp\", currentTest.userId), nil)\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar otpCredential *admin.OTPDto\n\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &otpCredential))\n\t\t\t\ts.NotNil(otpCredential)\n\t\t\t} else {\n\t\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *otpAdminSuite) TestOtpAdminHandler_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/otp\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should delete the otp credential\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if user has no otp credential\",\n\t\t\tuserId:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s/otp\", currentTest.userId), nil)\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif http.StatusNoContent == rec.Code {\n\t\t\t\tcred, err := s.Storage.GetPasswordCredentialPersister().GetByUserID(uuid.FromStringOrNil(currentTest.userId))\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Require().Nil(cred)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/passcode.go",
    "content": "package handler\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\tzeroLogger \"github.com/rs/zerolog/log\"\n\t\"github.com/sethvargo/go-limiter\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webhook\"\n\t\"github.com/teamhanko/hanko/backend/v2/mail\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"gopkg.in/gomail.v2\"\n)\n\ntype PasscodeHandler struct {\n\tmailer            mail.Mailer\n\trenderer          *mail.Renderer\n\tpasscodeGenerator crypto.PasscodeGenerator\n\tpersister         persistence.Persister\n\temailConfig       config.EmailDelivery\n\tserviceConfig     config.Service\n\tTTL               int\n\tsessionManager    session.Manager\n\tcfg               *config.Config\n\tauditLogger       auditlog.Logger\n\trateLimiter       limiter.Store\n}\n\nvar maxPasscodeTries = 3\n\nfunc NewPasscodeHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, mailer mail.Mailer, auditLogger auditlog.Logger) (*PasscodeHandler, error) {\n\trenderer, err := mail.NewRenderer()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create new renderer: %w\", err)\n\t}\n\tvar rateLimiter limiter.Store\n\tif cfg.RateLimiter.Enabled {\n\t\trateLimiter = rate_limiter.NewRateLimiter(cfg.RateLimiter, cfg.RateLimiter.PasscodeLimits)\n\t}\n\treturn &PasscodeHandler{\n\t\tmailer:            mailer,\n\t\trenderer:          renderer,\n\t\tpasscodeGenerator: crypto.NewNumericPasscodeGenerator(),\n\t\tpersister:         persister,\n\t\temailConfig:       cfg.EmailDelivery,\n\t\tserviceConfig:     cfg.Service,\n\t\tTTL:               cfg.Email.PasscodeTtl,\n\t\tsessionManager:    sessionManager,\n\t\tcfg:               cfg,\n\t\tauditLogger:       auditLogger,\n\t\trateLimiter:       rateLimiter,\n\t}, nil\n}\n\nfunc (h *PasscodeHandler) Init(c echo.Context) error {\n\tvar request dto.PasscodeInitRequest\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &request); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := c.Validate(request); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tuserId, err := uuid.FromString(request.UserId)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse userId as uuid\").SetInternal(err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\tif user == nil {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasscodeLoginInitFailed, nil, fmt.Errorf(\"unknown user\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusBadRequest).SetInternal(errors.New(\"user not found\"))\n\t}\n\n\tif h.rateLimiter != nil {\n\t\terr := rate_limiter.Limit(h.rateLimiter, userId, c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar emailId uuid.UUID\n\tif request.EmailId != nil {\n\t\temailId, err = uuid.FromString(*request.EmailId)\n\t\tif err != nil {\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse emailId as uuid\").SetInternal(err)\n\t\t}\n\t}\n\n\t// Determine where to send the passcode\n\tvar email *models.Email\n\tif !emailId.IsNil() {\n\t\t// Send the passcode to the specified email address\n\t\temail, err = h.persister.GetEmailPersister().Get(emailId)\n\t\tif email == nil {\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"the specified emailId is not available\")\n\t\t}\n\t} else if e := user.Emails.GetPrimary(); e == nil {\n\t\t// Workaround to support hanko element versions before v0.1.0-alpha:\n\t\t// If user has no primary email, check if a cookie with an email id is present\n\t\temailIdCookie, err := c.Cookie(\"hanko_email_id\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get email id cookie: %w\", err)\n\t\t}\n\n\t\tif emailIdCookie != nil && emailIdCookie.Value != \"\" {\n\t\t\temailId, err = uuid.FromString(emailIdCookie.Value)\n\t\t\tif err != nil {\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse emailId as uuid\").SetInternal(err)\n\t\t\t}\n\t\t\temail, err = h.persister.GetEmailPersister().Get(emailId)\n\t\t\tif email == nil {\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"the specified emailId is not available\")\n\t\t\t}\n\t\t} else {\n\t\t\t// Can't determine email address to which the passcode should be sent to\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"an emailId needs to be specified\")\n\t\t}\n\t} else {\n\t\t// Send the passcode to the primary email address\n\t\temail = e\n\t}\n\n\tsessionToken := h.GetSessionToken(c)\n\tif sessionToken != nil && sessionToken.Subject() != user.ID.String() {\n\t\t// if the user is logged in and the requested user in the request does not match the user from the session then sending and finalizing passcodes is not allowed\n\t\treturn echo.NewHTTPError(http.StatusForbidden).SetInternal(errors.New(\"session.userId does not match requested userId\"))\n\t}\n\n\tif email.User != nil && email.User.ID.String() != user.ID.String() {\n\t\treturn echo.NewHTTPError(http.StatusForbidden).SetInternal(errors.New(\"email address is assigned to another user\"))\n\t}\n\n\tpasscode, err := h.passcodeGenerator.Generate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate passcode: %w\", err)\n\t}\n\n\tpasscodeId, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create passcodeId: %w\", err)\n\t}\n\tnow := time.Now().UTC()\n\thashedPasscode, err := bcrypt.GenerateFromPassword([]byte(passcode), 12)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to hash passcode: %w\", err)\n\t}\n\tpasscodeModel := models.Passcode{\n\t\tID:        passcodeId,\n\t\tUserId:    &userId,\n\t\tEmailID:   &email.ID,\n\t\tTtl:       h.TTL,\n\t\tCode:      string(hashedPasscode),\n\t\tCreatedAt: now,\n\t\tUpdatedAt: now,\n\t}\n\n\terr = h.persister.GetPasscodePersister().Create(passcodeModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store passcode: %w\", err)\n\t}\n\n\tdurationTTL := time.Duration(h.TTL) * time.Second\n\tdata := map[string]interface{}{\n\t\t\"Code\":        passcode,\n\t\t\"ServiceName\": h.serviceConfig.Name,\n\t\t\"TTL\":         fmt.Sprintf(\"%.0f\", durationTTL.Minutes()),\n\t}\n\n\tlang := c.Request().Header.Get(\"Accept-Language\")\n\n\tsubject := h.renderer.Translate(lang, \"email_subject_login\", data)\n\tdata[\"Subject\"] = subject\n\n\tbodyPlain, err := h.renderer.RenderPlain(\"login\", lang, data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to render email template: %w\", err)\n\t}\n\n\tbodyHTML, err := h.renderer.RenderHTML(\"login\", lang, data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to render email template: %w\", err)\n\t}\n\n\twebhookData := webhook.EmailSend{\n\t\tSubject:          subject,\n\t\tBodyPlain:        bodyPlain,\n\t\tBody:             bodyHTML,\n\t\tToEmailAddress:   email.Address,\n\t\tDeliveredByHanko: true,\n\t\tAcceptLanguage:   lang,\n\t\tLanguage:         lang,\n\t\tType:             \"passcode\",\n\t\tData: webhook.PasscodeData{\n\t\t\tServiceName: h.cfg.Service.Name,\n\t\t\tOtpCode:     passcode,\n\t\t\tTTL:         h.TTL,\n\t\t\tValidUntil:  passcodeModel.CreatedAt.Add(time.Duration(h.TTL) * time.Second).UTC().Unix(),\n\t\t},\n\t}\n\n\tif h.cfg.EmailDelivery.Enabled {\n\t\tmessage := gomail.NewMessage()\n\t\tmessage.SetAddressHeader(\"To\", email.Address, \"\")\n\t\tmessage.SetAddressHeader(\"From\", h.emailConfig.FromAddress, h.emailConfig.FromName)\n\n\t\tmessage.SetHeader(\"Subject\", subject)\n\n\t\tmessage.SetBody(\"text/plain\", bodyPlain)\n\t\tmessage.AddAlternative(\"text/html\", bodyHTML)\n\n\t\terr = h.mailer.Send(message)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to send passcode: %w\", err)\n\t\t}\n\n\t\terr = utils.TriggerWebhooks(c, h.persister.GetConnection(), events.EmailSend, webhookData)\n\t\tif err != nil {\n\t\t\tzeroLogger.Warn().Err(err).Msg(\"failed to trigger webhook\")\n\t\t}\n\t} else {\n\t\twebhookData.DeliveredByHanko = false\n\t\terr = utils.TriggerWebhooks(c, h.persister.GetConnection(), events.EmailSend, webhookData)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to trigger webhook: %w\", err)\n\t\t}\n\t}\n\n\terr = h.auditLogger.Create(c, models.AuditLogPasscodeLoginInitSucceeded, user, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t}\n\n\treturn c.JSON(http.StatusOK, dto.PasscodeReturn{\n\t\tId:        passcodeId.String(),\n\t\tTTL:       h.TTL,\n\t\tCreatedAt: passcodeModel.CreatedAt,\n\t})\n}\n\nfunc (h *PasscodeHandler) Finish(c echo.Context) error {\n\tstartTime := time.Now().UTC()\n\tvar body dto.PasscodeFinishRequest\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := c.Validate(body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tpasscodeId, err := uuid.FromString(body.Id)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse passcodeId as uuid\").SetInternal(err)\n\t}\n\n\t// only if an internal server error occurs the transaction should be rolled back\n\tvar businessError error\n\ttransactionError := h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tpasscodePersister := h.persister.GetPasscodePersisterWithConnection(tx)\n\t\tuserPersister := h.persister.GetUserPersisterWithConnection(tx)\n\t\temailPersister := h.persister.GetEmailPersisterWithConnection(tx)\n\t\tprimaryEmailPersister := h.persister.GetPrimaryEmailPersisterWithConnection(tx)\n\t\tpasscode, err := passcodePersister.Get(passcodeId)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get passcode: %w\", err)\n\t\t}\n\t\tif passcode == nil {\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPasscodeLoginFinalFailed, nil, fmt.Errorf(\"unknown passcode\"))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t}\n\t\t\tbusinessError = echo.NewHTTPError(http.StatusUnauthorized, \"passcode not found\")\n\t\t\treturn nil\n\t\t}\n\n\t\tuserModel, err := userPersister.Get(*passcode.UserId)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t\t}\n\n\t\tlastVerificationTime := passcode.CreatedAt.Add(time.Duration(passcode.Ttl) * time.Second)\n\t\tif lastVerificationTime.Before(startTime) {\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPasscodeLoginFinalFailed, userModel, fmt.Errorf(\"timed out passcode\"))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t}\n\t\t\tbusinessError = echo.NewHTTPError(http.StatusRequestTimeout, \"passcode request timed out\").SetInternal(errors.New(fmt.Sprintf(\"createdAt: %s -> lastVerificationTime: %s\", passcode.CreatedAt, lastVerificationTime))) // TODO: maybe we should use BadRequest, because RequestTimeout might be to technical and can refer to different error\n\t\t\treturn nil\n\t\t}\n\n\t\terr = bcrypt.CompareHashAndPassword([]byte(passcode.Code), []byte(body.Code))\n\t\tif err != nil {\n\t\t\tpasscode.TryCount = passcode.TryCount + 1\n\n\t\t\tif passcode.TryCount >= maxPasscodeTries {\n\t\t\t\terr = passcodePersister.Delete(*passcode)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to delete passcode: %w\", err)\n\t\t\t\t}\n\t\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPasscodeLoginFinalFailed, userModel, fmt.Errorf(\"max attempts reached\"))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t\t}\n\t\t\t\tbusinessError = echo.NewHTTPError(http.StatusGone, \"max attempts reached\")\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\terr = passcodePersister.Update(*passcode)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to update passcode: %w\", err)\n\t\t\t}\n\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPasscodeLoginFinalFailed, userModel, fmt.Errorf(\"passcode invalid\"))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t}\n\t\t\tbusinessError = echo.NewHTTPError(http.StatusUnauthorized).SetInternal(errors.New(\"passcode invalid\"))\n\t\t\treturn nil\n\t\t}\n\n\t\terr = passcodePersister.Delete(*passcode)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete passcode: %w\", err)\n\t\t}\n\n\t\tif passcode.Email.User != nil && passcode.Email.User.ID.String() != userModel.ID.String() {\n\t\t\treturn echo.NewHTTPError(http.StatusForbidden, \"email address has been claimed by another user\")\n\t\t}\n\n\t\temailExistsForUser := false\n\t\tfor _, email := range userModel.Emails {\n\t\t\temailExistsForUser = email.ID == passcode.Email.ID\n\t\t\tif emailExistsForUser {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\texistingSessionToken := h.GetSessionToken(c)\n\t\t// return forbidden when none of these cases matches\n\t\tif !((existingSessionToken == nil && emailExistsForUser) || // normal login: when user logs in and the email used is associated with the user\n\t\t\t(existingSessionToken == nil && len(userModel.Emails) == 0) || // register: when user register and the user has no emails\n\t\t\t(existingSessionToken != nil && existingSessionToken.Subject() == userModel.ID.String())) { // add email through profile: when the user adds an email while having a session and the userIds requested in the passcode and the one in the session matches\n\t\t\treturn echo.NewHTTPError(http.StatusForbidden).SetInternal(errors.New(\"passcode finalization not allowed\"))\n\t\t}\n\n\t\twasUnverified := false\n\t\thasEmails := len(userModel.Emails) >= 1 // check if we need to trigger a UserCreate webhook or a EmailCreate one\n\n\t\tif !passcode.Email.Verified {\n\t\t\twasUnverified = true\n\n\t\t\t// Update email verified status and assign the email address to the user.\n\t\t\tpasscode.Email.Verified = true\n\t\t\tpasscode.Email.UserID = &userModel.ID\n\n\t\t\terr = emailPersister.Update(passcode.Email)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to update the email verified status: %w\", err)\n\t\t\t}\n\n\t\t\tif userModel.Emails.GetPrimary() == nil {\n\t\t\t\tprimaryEmail := models.NewPrimaryEmail(passcode.Email.ID, userModel.ID)\n\t\t\t\terr = primaryEmailPersister.Create(*primaryEmail)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create primary email: %w\", err)\n\t\t\t\t}\n\n\t\t\t\tuserModel.Emails = models.Emails{passcode.Email}\n\t\t\t\tuserModel.SetPrimaryEmail(primaryEmail)\n\t\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPrimaryEmailChanged, userModel, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogEmailVerified, userModel, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tvar emailJwt *dto.EmailJWT\n\t\tif e := userModel.Emails.GetPrimary(); e != nil {\n\t\t\temailJwt = dto.EmailJWTFromEmailModel(e)\n\t\t}\n\n\t\ttoken, rawToken, err := h.sessionManager.GenerateJWT(dto.UserJWT{\n\t\t\tUserID: passcode.UserId.String(),\n\t\t\tEmail:  emailJwt,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to generate jwt: %w\", err)\n\t\t}\n\n\t\tcookie, err := h.sessionManager.GenerateCookie(token)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create session token: %w\", err)\n\t\t}\n\n\t\terr = storeSession(h.cfg, h.persister, *passcode.UserId, rawToken, c, tx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store session in DB: %w\", err)\n\t\t}\n\n\t\tc.Response().Header().Set(\"X-Session-Lifetime\", fmt.Sprintf(\"%d\", cookie.MaxAge))\n\n\t\tif h.cfg.Session.EnableAuthTokenHeader {\n\t\t\tc.Response().Header().Set(\"X-Auth-Token\", token)\n\t\t} else {\n\t\t\tc.SetCookie(cookie)\n\t\t}\n\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPasscodeLoginFinalSucceeded, userModel, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\n\t\t// notify about email verification result. Last step to prevent a trigger and rollback scenario\n\t\tif h.cfg.Email.RequireVerification && wasUnverified {\n\t\t\tvar evt events.Event\n\n\t\t\tif hasEmails {\n\t\t\t\tevt = events.UserEmailCreate\n\t\t\t} else {\n\t\t\t\tevt = events.UserCreate\n\t\t\t}\n\n\t\t\tutils.NotifyUserChange(c, tx, h.persister, evt, userModel.ID)\n\t\t}\n\n\t\treturn c.JSON(http.StatusOK, dto.PasscodeReturn{\n\t\t\tId:        passcode.ID.String(),\n\t\t\tTTL:       passcode.Ttl,\n\t\t\tCreatedAt: passcode.CreatedAt,\n\t\t})\n\t})\n\n\tif businessError != nil {\n\t\treturn businessError\n\t}\n\n\treturn transactionError\n}\n\nfunc (h *PasscodeHandler) GetSessionToken(c echo.Context) jwt.Token {\n\tvar token jwt.Token\n\tsessionCookie, _ := c.Cookie(h.cfg.Session.Cookie.GetName())\n\t// we don't need to check the error, because when the cookie can not be found, the user is not logged in\n\tif sessionCookie != nil {\n\t\ttoken, _ = h.sessionManager.Verify(sessionCookie.Value)\n\t\t// we don't need to check the error, because when the token is not returned, the user is not logged in\n\t}\n\n\tif token == nil {\n\t\tauthorizationHeader := c.Request().Header.Get(\"Authorization\")\n\t\tsessionToken := strings.TrimPrefix(authorizationHeader, \"Bearer\")\n\t\tif strings.TrimSpace(sessionToken) != \"\" {\n\t\t\ttoken, _ = h.sessionManager.Verify(sessionToken)\n\t\t}\n\t}\n\n\treturn token\n}\n"
  },
  {
    "path": "backend/handler/passcode_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk/local_db\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nfunc TestPasscodeSuite(t *testing.T) {\n\ts := new(passcodeSuite)\n\ts.WithEmailServer = true\n\tsuite.Run(t, s)\n}\n\ntype passcodeSuite struct {\n\ttest.Suite\n}\n\nfunc (s *passcodeSuite) TestPasscodeHandler_Init() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/passcode\")\n\ts.Require().NoError(err)\n\n\tcfg := func() *config.Config {\n\t\tcfg := &test.DefaultConfig\n\t\tcfg.EmailDelivery.SMTP.Host = s.EmailServer.SmtpHost\n\t\tcfg.EmailDelivery.SMTP.Port = s.EmailServer.SmtpPort\n\t\treturn cfg\n\t}\n\n\te := NewPublicRouter(cfg(), s.Storage, nil, nil)\n\n\temailId := \"51b7c175-ceb6-45ba-aae6-0092221c1b84\"\n\tunknownEmailId := \"83618f24-2db8-4ea2-b370-ac8335f782d8\"\n\ttests := []struct {\n\t\tname                 string\n\t\tbody                 dto.PasscodeInitRequest\n\t\texpectedStatusCode   int\n\t\texpectedEmailAddress string\n\t}{\n\t\t{\n\t\t\tname: \"with userID and emailID\",\n\t\t\tbody: dto.PasscodeInitRequest{\n\t\t\t\tUserId:  \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\t\tEmailId: &emailId,\n\t\t\t},\n\t\t\texpectedStatusCode:   http.StatusOK,\n\t\t\texpectedEmailAddress: \"john.doe@example.com\",\n\t\t},\n\t\t{\n\t\t\tname: \"only with userID\",\n\t\t\tbody: dto.PasscodeInitRequest{\n\t\t\t\tUserId: \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\t},\n\t\t\texpectedStatusCode:   http.StatusOK,\n\t\t\texpectedEmailAddress: \"john.doe@example.com\",\n\t\t},\n\t\t{\n\t\t\tname: \"with unknown userID\",\n\t\t\tbody: dto.PasscodeInitRequest{\n\t\t\t\tUserId: \"83618f24-2db8-4ea2-b370-ac8335f782d8\",\n\t\t\t},\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname: \"with unknown emailID\",\n\t\t\tbody: dto.PasscodeInitRequest{\n\t\t\t\tUserId:  \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\t\tEmailId: &unknownEmailId,\n\t\t\t},\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\tbodyJson, err := json.Marshal(currentTest.body)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, \"/passcode/login/initialize\", bytes.NewReader(bodyJson))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif s.Equal(currentTest.expectedStatusCode, rec.Code) && currentTest.expectedStatusCode >= 200 && currentTest.expectedStatusCode <= 299 {\n\t\t\t\temails, err := s.EmailServer.GetEmails()\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\tmessages := emails.MailItems\n\t\t\t\ts.Require().Greater(len(messages), 0)\n\n\t\t\t\temailAddress := messages[len(messages)-1].ToAddresses[0]\n\t\t\t\ts.Equal(currentTest.expectedEmailAddress, emailAddress)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *passcodeSuite) TestPasscodeHandler_Finish() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/passcode\")\n\ts.Require().NoError(err)\n\n\tnow := time.Now().UTC()\n\n\thashedPasscode, err := bcrypt.GenerateFromPassword([]byte(\"123456\"), 12)\n\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\temailId := uuid.FromStringOrNil(\"51b7c175-ceb6-45ba-aae6-0092221c1b84\")\n\tpasscode := models.Passcode{\n\t\tID:        uuid.FromStringOrNil(\"a2383922-dea3-46c8-be17-85b267c0d135\"),\n\t\tUserId:    &userId,\n\t\tEmailID:   &emailId,\n\t\tTtl:       300,\n\t\tCode:      string(hashedPasscode),\n\t\tTryCount:  0,\n\t\tCreatedAt: now,\n\t\tUpdatedAt: now,\n\t}\n\n\tpasscodeWithExpiredTimeout := models.Passcode{\n\t\tID:        uuid.FromStringOrNil(\"a2383922-dea3-46c8-be17-85b267c0d135\"),\n\t\tUserId:    &userId,\n\t\tEmailID:   &emailId,\n\t\tTtl:       300,\n\t\tCode:      string(hashedPasscode),\n\t\tTryCount:  0,\n\t\tCreatedAt: now.Add(-500 * time.Second),\n\t\tUpdatedAt: now,\n\t}\n\n\temailIdNotAssigned := uuid.FromStringOrNil(\"7c4473b8-ddcc-480b-b01f-df89e99f74c9\")\n\tpasscodeForNonAssignedEmail := models.Passcode{\n\t\tID:        uuid.FromStringOrNil(\"494129d5-76de-4fae-b07d-f2a521e1804d\"),\n\t\tUserId:    &userId,\n\t\tEmailID:   &emailIdNotAssigned,\n\t\tTtl:       300,\n\t\tCode:      string(hashedPasscode),\n\t\tTryCount:  0,\n\t\tCreatedAt: now,\n\t\tUpdatedAt: now,\n\t}\n\n\tcfg := func() *config.Config {\n\t\treturn &test.DefaultConfig\n\t}\n\n\ttests := []struct {\n\t\tname                         string\n\t\tpasscodeId                   string\n\t\tretryCount                   int\n\t\tpasscode                     models.Passcode\n\t\tcode                         string\n\t\texpectedStatusCode           int\n\t\tcfg                          func() *config.Config\n\t\tuserId                       string\n\t\tsendSessionTokenInCookie     bool\n\t\tsendSessionTokenInAuthHeader bool\n\t}{\n\t\t{\n\t\t\tname:               \"finish successful\",\n\t\t\tpasscodeId:         \"a2383922-dea3-46c8-be17-85b267c0d135\",\n\t\t\tpasscode:           passcode,\n\t\t\tcode:               \"123456\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\tcfg:                cfg,\n\t\t},\n\t\t{\n\t\t\tname:               \"finish successful with token in header\",\n\t\t\tpasscodeId:         \"a2383922-dea3-46c8-be17-85b267c0d135\",\n\t\t\tpasscode:           passcode,\n\t\t\tcode:               \"123456\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\tcfg: func() *config.Config {\n\t\t\t\tc := test.DefaultConfig\n\t\t\t\tc.Session.EnableAuthTokenHeader = true\n\t\t\t\treturn &c\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:               \"with wrong code\",\n\t\t\tpasscodeId:         \"a2383922-dea3-46c8-be17-85b267c0d135\",\n\t\t\tpasscode:           passcode,\n\t\t\tcode:               \"654321\",\n\t\t\texpectedStatusCode: http.StatusUnauthorized,\n\t\t\tcfg:                cfg,\n\t\t},\n\t\t{\n\t\t\tname:               \"with wrong code 3 times\",\n\t\t\tpasscodeId:         \"a2383922-dea3-46c8-be17-85b267c0d135\",\n\t\t\tretryCount:         2,\n\t\t\tpasscode:           passcode,\n\t\t\tcode:               \"654321\",\n\t\t\texpectedStatusCode: http.StatusGone,\n\t\t\tcfg:                cfg,\n\t\t},\n\t\t{\n\t\t\tname:               \"with wrong passcode ID\",\n\t\t\tpasscodeId:         \"297cfc1b-98cc-4ae1-bc83-bcafc7f0e876\",\n\t\t\tpasscode:           passcode,\n\t\t\tcode:               \"123456\",\n\t\t\texpectedStatusCode: http.StatusUnauthorized,\n\t\t\tcfg:                cfg,\n\t\t},\n\t\t{\n\t\t\tname:               \"after passcode expired\",\n\t\t\tpasscodeId:         \"a2383922-dea3-46c8-be17-85b267c0d135\",\n\t\t\tpasscode:           passcodeWithExpiredTimeout,\n\t\t\tcode:               \"123456\",\n\t\t\texpectedStatusCode: http.StatusRequestTimeout,\n\t\t\tcfg:                cfg,\n\t\t},\n\t\t{\n\t\t\tname:                     \"create email with session in cookie\",\n\t\t\tpasscodeId:               \"494129d5-76de-4fae-b07d-f2a521e1804d\",\n\t\t\tpasscode:                 passcodeForNonAssignedEmail,\n\t\t\tcode:                     \"123456\",\n\t\t\texpectedStatusCode:       http.StatusOK,\n\t\t\tcfg:                      cfg,\n\t\t\tuserId:                   \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tsendSessionTokenInCookie: true,\n\t\t},\n\t\t{\n\t\t\tname:                     \"do not create email with wrong session in cookie\",\n\t\t\tpasscodeId:               \"494129d5-76de-4fae-b07d-f2a521e1804d\",\n\t\t\tpasscode:                 passcodeForNonAssignedEmail,\n\t\t\tcode:                     \"123456\",\n\t\t\texpectedStatusCode:       http.StatusForbidden,\n\t\t\tcfg:                      cfg,\n\t\t\tuserId:                   \"851842a9-db50-49b5-aa00-1c447c31d819\",\n\t\t\tsendSessionTokenInCookie: true,\n\t\t},\n\t\t{\n\t\t\tname:                         \"create email with session in Authorization header\",\n\t\t\tpasscodeId:                   \"494129d5-76de-4fae-b07d-f2a521e1804d\",\n\t\t\tpasscode:                     passcodeForNonAssignedEmail,\n\t\t\tcode:                         \"123456\",\n\t\t\texpectedStatusCode:           http.StatusOK,\n\t\t\tcfg:                          cfg,\n\t\t\tuserId:                       \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tsendSessionTokenInAuthHeader: true,\n\t\t},\n\t\t{\n\t\t\tname:                         \"do not create email with wrong session in Authorization header\",\n\t\t\tpasscodeId:                   \"494129d5-76de-4fae-b07d-f2a521e1804d\",\n\t\t\tpasscode:                     passcodeForNonAssignedEmail,\n\t\t\tcode:                         \"123456\",\n\t\t\texpectedStatusCode:           http.StatusForbidden,\n\t\t\tcfg:                          cfg,\n\t\t\tuserId:                       \"851842a9-db50-49b5-aa00-1c447c31d819\",\n\t\t\tsendSessionTokenInAuthHeader: true,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.SetupTest()\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/passcode\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\tjwkManager, err := local_db.NewDefaultManager(test.DefaultConfig.Secrets.Keys, s.Storage.GetJwkPersister())\n\t\t\ts.Require().NoError(err)\n\t\t\tsessionManager, err := session.NewManager(jwkManager, test.DefaultConfig)\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewPublicRouter(currentTest.cfg(), s.Storage, nil, nil)\n\n\t\t\t// Setup passcode\n\t\t\terr = s.Storage.GetPasscodePersister().Create(currentTest.passcode)\n\t\t\ts.Require().NoError(err)\n\n\t\t\tbody := dto.PasscodeFinishRequest{\n\t\t\t\tId:   currentTest.passcodeId,\n\t\t\t\tCode: currentTest.code,\n\t\t\t}\n\t\t\tbodyJson, err := json.Marshal(body)\n\t\t\ts.Require().NoError(err)\n\n\t\t\tresponseCode := 0\n\t\t\tvar response *http.Response\n\t\t\tvar headers http.Header\n\t\t\tfor i := 0; i <= currentTest.retryCount; i++ {\n\t\t\t\treq := httptest.NewRequest(http.MethodPost, \"/passcode/login/finalize\", bytes.NewReader(bodyJson))\n\t\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\t\tif currentTest.sendSessionTokenInAuthHeader {\n\t\t\t\t\tsessionToken, _, err := sessionManager.GenerateJWT(dto.UserJWT{\n\t\t\t\t\t\tUserID: currentTest.userId,\n\t\t\t\t\t})\n\t\t\t\t\ts.Require().NoError(err)\n\t\t\t\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", sessionToken))\n\t\t\t\t}\n\n\t\t\t\tif currentTest.sendSessionTokenInCookie {\n\t\t\t\t\tsessionToken, _, err := sessionManager.GenerateJWT(dto.UserJWT{\n\t\t\t\t\t\tUserID: currentTest.userId,\n\t\t\t\t\t})\n\t\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\t\tsessionCookie, err := sessionManager.GenerateCookie(sessionToken)\n\t\t\t\t\ts.Require().NoError(err)\n\t\t\t\t\treq.AddCookie(sessionCookie)\n\t\t\t\t}\n\t\t\t\trec := httptest.NewRecorder()\n\n\t\t\t\te.ServeHTTP(rec, req)\n\t\t\t\tresponseCode = rec.Code\n\t\t\t\tresponse = rec.Result()\n\t\t\t\theaders = rec.Header()\n\t\t\t}\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, responseCode)\n\n\t\t\tif currentTest.cfg().Session.EnableAuthTokenHeader {\n\t\t\t\ts.Empty(response.Cookies())\n\t\t\t\ttoken := headers.Get(\"X-Auth-Token\")\n\t\t\t\ts.NotEmpty(token)\n\t\t\t\ts.Regexp(\".*\\\\..*\\\\..*\", token)\n\t\t\t}\n\n\t\t\t// remove passcode\n\t\t\t_ = s.Storage.GetPasscodePersister().Delete(currentTest.passcode)\n\t\t\ts.TearDownTest()\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/password.go",
    "content": "package handler\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"unicode/utf8\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/sethvargo/go-limiter\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\ntype PasswordHandler struct {\n\tpersister      persistence.Persister\n\tsessionManager session.Manager\n\tcfg            *config.Config\n\tauditLogger    auditlog.Logger\n\trateLimiter    limiter.Store\n}\n\nfunc NewPasswordHandler(persister persistence.Persister, sessionManager session.Manager, cfg *config.Config, auditLogger auditlog.Logger) *PasswordHandler {\n\tvar rateLimiter limiter.Store\n\tif cfg.RateLimiter.Enabled {\n\t\trateLimiter = rate_limiter.NewRateLimiter(cfg.RateLimiter, cfg.RateLimiter.PasswordLimits)\n\t}\n\treturn &PasswordHandler{\n\t\tpersister:      persister,\n\t\tsessionManager: sessionManager,\n\t\tcfg:            cfg,\n\t\tauditLogger:    auditLogger,\n\t\trateLimiter:    rateLimiter,\n\t}\n}\n\ntype PasswordSetBody struct {\n\tUserID   string `json:\"user_id\" validate:\"required,uuid\"`\n\tPassword string `json:\"password\" validate:\"required\"`\n}\n\nfunc (h *PasswordHandler) Set(c echo.Context) error {\n\tvar body PasswordSetBody\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := c.Validate(body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"missing or malformed jwt\")\n\t}\n\n\tsessionUserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse userId as uuid\").SetInternal(err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(uuid.FromStringOrNil(body.UserID))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tpwBytes := []byte(body.Password)\n\tif utf8.RuneCountInString(body.Password) < h.cfg.Password.MinLength { // use utf8.RuneCountInString, so utf8 characters would count as 1\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordSetFailed, user, fmt.Errorf(\"password too short\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf(\"password must be at least %d characters long\", h.cfg.Password.MinLength))\n\t}\n\n\tif len(pwBytes) > 72 {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordSetFailed, user, fmt.Errorf(\"password too long\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"password must not be longer than 72 bytes\")\n\t}\n\n\tif user == nil {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordSetFailed, user, fmt.Errorf(\"unknown user: %s\", body.UserID))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusUnauthorized).SetInternal(errors.New(fmt.Sprintf(\"user %s not found \", sessionUserId)))\n\t}\n\n\tif sessionUserId != user.ID {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordSetFailed, user, fmt.Errorf(\"wrong user: expected %s -> got %s\", sessionUserId, user.ID))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusForbidden).SetInternal(errors.New(fmt.Sprintf(\"session.userId %s tried to set password credentials for body.userId %s\", sessionUserId, user.ID)))\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tpwPersister := h.persister.GetPasswordCredentialPersisterWithConnection(tx)\n\t\tpw, err := pwPersister.GetByUserID(user.ID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get credential: %w\", err)\n\t\t}\n\n\t\thashedPassword, err := bcrypt.GenerateFromPassword(pwBytes, 12)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to hash password: %s\", err)\n\t\t}\n\n\t\tnewPw := models.PasswordCredential{\n\t\t\tUserId:   uuid.FromStringOrNil(body.UserID),\n\t\t\tPassword: string(hashedPassword),\n\t\t}\n\n\t\tif pw == nil {\n\t\t\terr = pwPersister.Create(newPw)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create password: %w\", err)\n\t\t\t} else {\n\t\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPasswordSetSucceeded, user, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t\t}\n\t\t\t\treturn c.JSON(http.StatusCreated, nil)\n\t\t\t}\n\t\t} else {\n\t\t\tnewPw.ID = pw.ID\n\t\t\terr = pwPersister.Update(newPw)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to set password: %w\", err)\n\t\t\t} else {\n\t\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogPasswordSetSucceeded, user, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t\t\t}\n\t\t\t\treturn c.JSON(http.StatusOK, nil)\n\t\t\t}\n\t\t}\n\t})\n}\n\ntype PasswordLoginBody struct {\n\tUserId   string `json:\"user_id\" validate:\"required,uuid\"`\n\tPassword string `json:\"password\" validate:\"required\"`\n}\n\nfunc (h *PasswordHandler) Login(c echo.Context) error {\n\tvar body PasswordLoginBody\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := c.Validate(body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tuserId, err := uuid.FromString(body.UserId)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"user_id is not a uuid\").SetInternal(err)\n\t}\n\n\tif h.rateLimiter != nil {\n\t\terr := rate_limiter.Limit(h.rateLimiter, userId, c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\tif user == nil {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordLoginFailed, nil, fmt.Errorf(\"unknown user: %s\", userId))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusUnauthorized).SetInternal(errors.New(\"user not found\"))\n\t}\n\n\tpwBytes := []byte(body.Password)\n\tif len(pwBytes) > 72 {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordLoginFailed, user, errors.New(\"password too long\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"password must not be longer than 72 bytes\")\n\t}\n\n\tpw, err := h.persister.GetPasswordCredentialPersister().GetByUserID(uuid.FromStringOrNil(body.UserId))\n\tif pw == nil {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordLoginFailed, user, fmt.Errorf(\"user has no password credential\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusUnauthorized).SetInternal(errors.New(fmt.Sprintf(\"no password credential found for: %s\", body.UserId)))\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error retrieving credential: %w\", err)\n\t}\n\n\tif err = bcrypt.CompareHashAndPassword([]byte(pw.Password), pwBytes); err != nil {\n\t\terr = h.auditLogger.Create(c, models.AuditLogPasswordLoginFailed, user, fmt.Errorf(\"password hash not equal\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusUnauthorized).SetInternal(err)\n\t}\n\n\tvar emailJwt *dto.EmailJWT\n\tif e := user.Emails.GetPrimary(); e != nil {\n\t\temailJwt = dto.EmailJWTFromEmailModel(e)\n\t}\n\n\ttoken, rawToken, err := h.sessionManager.GenerateJWT(dto.UserJWT{\n\t\tUserID: pw.UserId.String(),\n\t\tEmail:  emailJwt,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate jwt: %w\", err)\n\t}\n\n\tcookie, err := h.sessionManager.GenerateCookie(token)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create session cookie: %w\", err)\n\t}\n\n\terr = storeSession(h.cfg, h.persister, userId, rawToken, c, h.persister.GetConnection())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store session in DB: %w\", err)\n\t}\n\n\tc.Response().Header().Set(\"X-Session-Lifetime\", fmt.Sprintf(\"%d\", cookie.MaxAge))\n\n\tif h.cfg.Session.EnableAuthTokenHeader {\n\t\tc.Response().Header().Set(\"X-Auth-Token\", token)\n\t} else {\n\t\tc.SetCookie(cookie)\n\t}\n\n\terr = h.auditLogger.Create(c, models.AuditLogPasswordLoginSucceeded, user, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create audit log: %w\", err)\n\t}\n\n\treturn c.JSON(http.StatusOK, nil)\n}\n"
  },
  {
    "path": "backend/handler/password_admin.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"net/http\"\n)\n\ntype PasswordAdminHandler interface {\n\tGet(ctx echo.Context) error\n\tCreate(ctx echo.Context) error\n\tUpdate(ctx echo.Context) error\n\tDelete(ctx echo.Context) error\n}\n\ntype passwordAdminHandler struct {\n\tpersister       persistence.Persister\n\tpasswordService services.Password\n}\n\nfunc NewPasswordAdminHandler(persister persistence.Persister) PasswordAdminHandler {\n\treturn &passwordAdminHandler{\n\t\tpersister:       persister,\n\t\tpasswordService: services.NewPasswordService(persister),\n\t}\n}\n\nfunc (h *passwordAdminHandler) Get(ctx echo.Context) error {\n\tgetDto, err := loadDto[admin.GetPasswordCredentialRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(getDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tcredential, err := h.persister.GetPasswordCredentialPersister().GetByUserID(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif credential == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tdto := admin.PasswordCredential{\n\t\tID:        credential.ID,\n\t\tCreatedAt: credential.CreatedAt,\n\t\tUpdatedAt: credential.UpdatedAt,\n\t}\n\n\treturn ctx.JSON(http.StatusOK, dto)\n}\n\nfunc (h *passwordAdminHandler) Create(ctx echo.Context) error {\n\tcreateDto, err := loadDto[admin.CreateOrUpdatePasswordCredentialRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(createDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\texistingCredential, err := h.persister.GetPasswordCredentialPersister().GetByUserID(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif existingCredential != nil {\n\t\treturn echo.NewHTTPError(http.StatusConflict)\n\t}\n\n\terr = h.passwordService.CreatePassword(h.persister.GetConnection(), userID, createDto.Password)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcredential, err := h.persister.GetPasswordCredentialPersister().GetByUserID(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdto := admin.PasswordCredential{\n\t\tID:        credential.ID,\n\t\tCreatedAt: credential.CreatedAt,\n\t\tUpdatedAt: credential.UpdatedAt,\n\t}\n\n\treturn ctx.JSON(http.StatusOK, dto)\n}\n\nfunc (h *passwordAdminHandler) Update(ctx echo.Context) error {\n\tupdateDto, err := loadDto[admin.CreateOrUpdatePasswordCredentialRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(updateDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tcredential, err := h.persister.GetPasswordCredentialPersister().GetByUserID(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif credential == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\terr = h.passwordService.UpdatePassword(h.persister.GetConnection(), credential, updateDto.Password)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcredential, err = h.persister.GetPasswordCredentialPersister().GetByUserID(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdto := admin.PasswordCredential{\n\t\tID:        credential.ID,\n\t\tCreatedAt: credential.CreatedAt,\n\t\tUpdatedAt: credential.UpdatedAt,\n\t}\n\n\treturn ctx.JSON(http.StatusOK, dto)\n}\n\nfunc (h *passwordAdminHandler) Delete(ctx echo.Context) error {\n\tgetDto, err := loadDto[admin.GetPasswordCredentialRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(getDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tcredential, err := h.persister.GetPasswordCredentialPersister().GetByUserID(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif credential == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\terr = h.persister.GetPasswordCredentialPersister().Delete(*credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.NoContent(http.StatusNoContent)\n}\n"
  },
  {
    "path": "backend/handler/password_admin_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestPasswordAdminSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(passwordAdminSuite))\n}\n\ntype passwordAdminSuite struct {\n\ttest.Suite\n}\n\nfunc (s *passwordAdminSuite) TestPasswordAdminHandler_Get() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/password\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:   \"should return password credential\",\n\t\t\tuserId: \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if user has no password\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s/password\", currentTest.userId), nil)\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar passwordCredential *admin.PasswordCredential\n\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &passwordCredential))\n\t\t\t\ts.NotNil(passwordCredential)\n\t\t\t} else {\n\t\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *passwordAdminSuite) TestPasswordAdminHandler_Create() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/password\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\tpassword           string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:     \"should create a new password credential\",\n\t\t\tuserId:   \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpassword: \"superSecure\",\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if user already has a password\",\n\t\t\tuserId:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusConflict,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty password\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\tpassword:           \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ttestDto := admin.CreateOrUpdatePasswordCredentialRequestDto{\n\t\t\t\tGetPasswordCredentialRequestDto: admin.GetPasswordCredentialRequestDto{\n\t\t\t\t\tUserID: currentTest.userId,\n\t\t\t\t},\n\t\t\t\tPassword: currentTest.password,\n\t\t\t}\n\n\t\t\ttestJson, err := json.Marshal(testDto)\n\t\t\ts.Require().NoError(err)\n\t\t\treq := httptest.NewRequest(http.MethodPost, fmt.Sprintf(\"/users/%s/password\", currentTest.userId), bytes.NewReader(testJson))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar passwordCredential *admin.PasswordCredential\n\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &passwordCredential))\n\t\t\t\ts.NotNil(passwordCredential)\n\n\t\t\t\tcred, err := s.Storage.GetPasswordCredentialPersister().GetByUserID(uuid.FromStringOrNil(currentTest.userId))\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Require().NotNil(cred)\n\t\t\t} else {\n\t\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *passwordAdminSuite) TestPasswordAdminHandler_Update() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/password\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\toldHashedPassword  string\n\t\tpassword           string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:              \"should update a password credential\",\n\t\t\tuserId:            \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\toldHashedPassword: \"$2a$12$Cf7k.dG6pznTUJ5u2u1pgu6I4VXH5.9O0NZsDk8TwWwyBkZovYVli\",\n\t\t\tpassword:          \"superSecure\",\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if user already has no password\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\tpassword:           \"superSecure\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty password\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\tpassword:           \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ttestDto := admin.CreateOrUpdatePasswordCredentialRequestDto{\n\t\t\t\tGetPasswordCredentialRequestDto: admin.GetPasswordCredentialRequestDto{\n\t\t\t\t\tUserID: currentTest.userId,\n\t\t\t\t},\n\t\t\t\tPassword: currentTest.password,\n\t\t\t}\n\n\t\t\ttestJson, err := json.Marshal(testDto)\n\t\t\ts.Require().NoError(err)\n\t\t\treq := httptest.NewRequest(http.MethodPut, fmt.Sprintf(\"/users/%s/password\", currentTest.userId), bytes.NewReader(testJson))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar passwordCredential *admin.PasswordCredential\n\t\t\t\ts.NoError(json.Unmarshal(rec.Body.Bytes(), &passwordCredential))\n\t\t\t\ts.NotNil(passwordCredential)\n\n\t\t\t\tcred, err := s.Storage.GetPasswordCredentialPersister().GetByUserID(uuid.FromStringOrNil(currentTest.userId))\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.NotEqual(currentTest.oldHashedPassword, cred.Password)\n\t\t\t} else {\n\t\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *passwordAdminSuite) TestPasswordAdminHandler_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/password\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserId             string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should delete a password credential\",\n\t\t\tuserId:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if user already has no password\",\n\t\t\tuserId:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserId:             \"customUserId\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserId:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserId:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s/password\", currentTest.userId), nil)\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Require().Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\tif http.StatusNoContent == rec.Code {\n\t\t\t\tcred, err := s.Storage.GetPasswordCredentialPersister().GetByUserID(uuid.FromStringOrNil(currentTest.userId))\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Require().Nil(cred)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/password_test.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestPasswordSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(passwordSuite))\n}\n\ntype passwordSuite struct {\n\ttest.Suite\n}\n\nfunc (s *passwordSuite) TestPasswordHandler_Set_Create() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping in short mode\")\n\t}\n\n\tuserWithNoPassword := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\tuserWithPassword := uuid.FromStringOrNil(\"38bf5a00-d7ea-40a5-a5de-48722c148925\")\n\tunknownUser := uuid.FromStringOrNil(\"6a565180-2366-45b1-8785-39f7902c7f2e\")\n\n\tcfg := &test.DefaultConfig\n\tcfg.Password.Enabled = true\n\tcfg.Password.MinLength = 8\n\n\ttests := []struct {\n\t\tname         string\n\t\tbody         string\n\t\tuserId       uuid.UUID\n\t\texpectedCode int\n\t}{\n\t\t{\n\t\t\tname:         \"should create a password successful\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"verybadpassword\"}`, userWithNoPassword),\n\t\t\tuserId:       userWithNoPassword,\n\t\t\texpectedCode: http.StatusCreated,\n\t\t},\n\t\t{\n\t\t\tname:         \"should update a password successful\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"verybadpassword\"}`, userWithPassword),\n\t\t\tuserId:       userWithPassword,\n\t\t\texpectedCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:         \"should not create a password that is too short\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"very\"}`, userWithNoPassword),\n\t\t\tuserId:       userWithNoPassword,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:         \"should not create a password that is too long\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"thisIsAVeryLongPasswordThatIsUsedToTestIfAnErrorWillBeReturnedForTooLongPasswords\"}`, userWithNoPassword),\n\t\t\tuserId:       userWithNoPassword,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:         \"should not create a password for an unknown user\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"verybadpassword\"}`, unknownUser),\n\t\t\tuserId:       unknownUser,\n\t\t\texpectedCode: http.StatusUnauthorized,\n\t\t},\n\t\t{\n\t\t\tname:         \"should not create a password for a different user\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"verybadpassword\"}`, userWithNoPassword),\n\t\t\tuserId:       userWithPassword,\n\t\t\texpectedCode: http.StatusForbidden,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.SetupTest()\n\t\t\tdefer s.TearDownTest()\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/password\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\tcookie, err := generateSessionCookie(s.Storage, currentTest.userId)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPut, \"/password\", strings.NewReader(currentTest.body))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\treq.AddCookie(cookie)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te := NewPublicRouter(cfg, s.Storage, nil, nil)\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedCode, rec.Code)\n\t\t})\n\t}\n}\n\nfunc (s *passwordSuite) TestPasswordHandler_Login() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/password\")\n\ts.Require().NoError(err)\n\n\tuserWithPassword := uuid.FromStringOrNil(\"38bf5a00-d7ea-40a5-a5de-48722c148925\")\n\tunknownUser := uuid.FromStringOrNil(\"6a565180-2366-45b1-8785-39f7902c7f2e\")\n\n\tcfg := func() *config.Config {\n\t\tcfg := test.DefaultConfig\n\t\tcfg.Password.Enabled = true\n\t\treturn &cfg\n\t}\n\n\ttests := []struct {\n\t\tname                string\n\t\tbody                string\n\t\texpectedCode        int\n\t\tcfg                 func() *config.Config\n\t\tshouldContainCookie bool\n\t}{\n\t\t{\n\t\t\tname:                \"should login successful\",\n\t\t\tbody:                fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"SuperSecure\"}`, userWithPassword),\n\t\t\tcfg:                 cfg,\n\t\t\texpectedCode:        http.StatusOK,\n\t\t\tshouldContainCookie: true,\n\t\t},\n\t\t{\n\t\t\tname: \"should login successful with token in header\",\n\t\t\tbody: fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"SuperSecure\"}`, userWithPassword),\n\t\t\tcfg: func() *config.Config {\n\t\t\t\tcfg := test.DefaultConfig\n\t\t\t\tcfg.Password.Enabled = true\n\t\t\t\tcfg.Session.EnableAuthTokenHeader = true\n\t\t\t\treturn &cfg\n\t\t\t},\n\t\t\texpectedCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:         \"should not login with wrong password\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"verybadpassword\"}`, userWithPassword),\n\t\t\tcfg:          cfg,\n\t\t\texpectedCode: http.StatusUnauthorized,\n\t\t},\n\t\t{\n\t\t\tname:         \"should not login with non existing user\",\n\t\t\tbody:         fmt.Sprintf(`{\"user_id\": \"%s\", \"password\": \"verybadpassword\"}`, unknownUser),\n\t\t\tcfg:          cfg,\n\t\t\texpectedCode: http.StatusUnauthorized,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodPost, \"/password/login\", strings.NewReader(currentTest.body))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te := NewPublicRouter(currentTest.cfg(), s.Storage, nil, nil)\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\tif s.Equal(currentTest.expectedCode, rec.Code) {\n\t\t\t\tif currentTest.shouldContainCookie {\n\t\t\t\t\ts.Empty(rec.Header().Get(\"X-Auth-Token\"))\n\t\t\t\t\tcookies := rec.Result().Cookies()\n\t\t\t\t\tif s.NotEmpty(cookies) {\n\t\t\t\t\t\tfor _, cookie := range cookies {\n\t\t\t\t\t\t\tif cookie.Name == \"hanko\" {\n\t\t\t\t\t\t\t\ts.Regexp(\".*\\\\..*\\\\..*\", cookie.Value) // check if cookie contains a jwt\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if currentTest.cfg().Session.EnableAuthTokenHeader {\n\t\t\t\t\ts.Empty(rec.Result().Cookies())\n\t\t\t\t\ttoken := rec.Header().Get(\"X-Auth-Token\")\n\t\t\t\t\ts.NotEmpty(token)\n\t\t\t\t\ts.Regexp(\".*\\\\..*\\\\..*\", token)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestMaxPasswordLength bcrypt since version 0.5.0 only accepts passwords at least 72 bytes long. This test documents this behaviour.\nfunc TestMaxPasswordLength(t *testing.T) {\n\ttests := []struct {\n\t\tname             string\n\t\tcreationPassword string\n\t\tloginPassword    string\n\t\twantErr          bool\n\t}{\n\t\t{\n\t\t\tname:             \"login password 72 bytes long\",\n\t\t\tcreationPassword: \"012345678901234567890123456789012345678901234567890123456789012345678901\",\n\t\t\tloginPassword:    \"012345678901234567890123456789012345678901234567890123456789012345678901\",\n\t\t\twantErr:          false,\n\t\t},\n\t\t{\n\t\t\tname:             \"login password over 72 bytes long\",\n\t\t\tcreationPassword: \"0123456789012345678901234567890123456789012345678901234567890123456789012\",\n\t\t\tloginPassword:    \"0123456789012345678901234567890123456789012345678901234567890123456789012\",\n\t\t\twantErr:          true,\n\t\t},\n\t}\n\n\tfor _, passwordTest := range tests {\n\t\tt.Run(passwordTest.name, func(t *testing.T) {\n\t\t\thash, err := bcrypt.GenerateFromPassword([]byte(passwordTest.creationPassword), 12)\n\t\t\tif passwordTest.wantErr {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\n\t\t\terr = bcrypt.CompareHashAndPassword(hash, []byte(passwordTest.loginPassword))\n\t\t\tif passwordTest.wantErr {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/public_router.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/labstack/echo/v4/middleware\"\n\t\"github.com/sethvargo/go-limiter\"\n\t\"github.com/sethvargo/go-limiter/httplimit\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow_locker\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/services\"\n\t\"github.com/teamhanko/hanko/backend/v2/mail\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\thankoMiddleware \"github.com/teamhanko/hanko/backend/v2/middleware\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/template\"\n)\n\nfunc NewPublicRouter(cfg *config.Config, persister persistence.Persister, prometheus echo.MiddlewareFunc, authenticatorMetadata mapper.AuthenticatorMetadata) *echo.Echo {\n\te := echo.New()\n\n\te.Renderer = template.NewTemplateRenderer()\n\n\te.Static(\"/flowpilot\", \"flow_api/static\") // TODO: remove!\n\n\tauditLogger := auditlog.NewLogger(persister, cfg.AuditLog)\n\n\temailService, _ := services.NewEmailService(*cfg)\n\tpasscodeService := services.NewPasscodeService(*cfg, *emailService, persister)\n\tpasswordService := services.NewPasswordService(persister)\n\twebauthnService := services.NewWebauthnService(*cfg, persister)\n\tsecurityNotificationService := services.NewSecurityNotificationService(*cfg, *emailService, persister, auditLogger)\n\n\tjwkManager, err := jwk.NewManager(cfg.Secrets, persister)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to create jwk manager: %w\", err))\n\t}\n\tsessionManager, err := session.NewManager(jwkManager, *cfg)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to create session generator: %w\", err))\n\t}\n\n\tvar otpRateLimiter limiter.Store\n\tvar passcodeRateLimiter limiter.Store\n\tvar passwordRateLimiter limiter.Store\n\tvar tokenExchangeRateLimiter limiter.Store\n\tif cfg.RateLimiter.Enabled {\n\t\totpRateLimiter = rate_limiter.NewRateLimiter(cfg.RateLimiter, cfg.RateLimiter.OTPLimits)\n\t\tpasscodeRateLimiter = rate_limiter.NewRateLimiter(cfg.RateLimiter, cfg.RateLimiter.PasscodeLimits)\n\t\tpasswordRateLimiter = rate_limiter.NewRateLimiter(cfg.RateLimiter, cfg.RateLimiter.PasswordLimits)\n\t\ttokenExchangeRateLimiter = rate_limiter.NewRateLimiter(cfg.RateLimiter, cfg.RateLimiter.TokenLimits)\n\t}\n\n\tsamlService := saml.NewSamlService(cfg, persister)\n\n\tflowAPIHandler := flow_api.FlowPilotHandler{\n\t\tPersister:                   persister,\n\t\tCfg:                         *cfg,\n\t\tPasscodeService:             passcodeService,\n\t\tSecurityNotificationService: securityNotificationService,\n\t\tPasswordService:             passwordService,\n\t\tWebauthnService:             webauthnService,\n\t\tSessionManager:              sessionManager,\n\t\tOTPRateLimiter:              otpRateLimiter,\n\t\tPasscodeRateLimiter:         passcodeRateLimiter,\n\t\tPasswordRateLimiter:         passwordRateLimiter,\n\t\tTokenExchangeRateLimiter:    tokenExchangeRateLimiter,\n\t\tAuthenticatorMetadata:       authenticatorMetadata,\n\t\tAuditLogger:                 auditLogger,\n\t\tSamlService:                 samlService,\n\t}\n\n\tflowLocker, err := flow_locker.NewFlowLocker(cfg.FlowLocker)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to initialize flow locker: %w\", err))\n\t}\n\tflowAPIHandler.FlowLocker = flowLocker\n\n\tif cfg.Saml.Enabled {\n\t\tsaml.CreateSamlRoutes(e, sessionManager, auditLogger, samlService)\n\t}\n\n\tsessionMiddleware := hankoMiddleware.Session(cfg, persister, sessionManager)\n\n\twebhookMiddleware := hankoMiddleware.WebhookMiddleware(cfg, jwkManager, persister)\n\n\te.POST(\"/registration\", flowAPIHandler.RegistrationFlowHandler, webhookMiddleware)\n\te.POST(\"/login\", flowAPIHandler.LoginFlowHandler, webhookMiddleware)\n\te.POST(\"/profile\", flowAPIHandler.ProfileFlowHandler, webhookMiddleware)\n\n\tif cfg.Saml.Enabled {\n\t\te.POST(\"/token_exchange\", flowAPIHandler.TokenExchangeFlowHandler, webhookMiddleware)\n\t}\n\n\te.HideBanner = true\n\tg := e.Group(\"\")\n\n\te.HTTPErrorHandler = dto.NewHTTPErrorHandler(dto.HTTPErrorHandlerConfig{Debug: false, Logger: e.Logger})\n\te.Use(middleware.RequestID())\n\tif cfg.Log.LogHealthAndMetrics {\n\t\te.Use(hankoMiddleware.GetLoggerMiddleware())\n\t} else {\n\t\tg.Use(hankoMiddleware.GetLoggerMiddleware())\n\t}\n\n\texposeHeader := []string{\n\t\thttplimit.HeaderRetryAfter,\n\t\thttplimit.HeaderRateLimitLimit,\n\t\thttplimit.HeaderRateLimitRemaining,\n\t\thttplimit.HeaderRateLimitReset,\n\t\t\"X-Session-Lifetime\",\n\t\t\"X-Session-Retention\",\n\t}\n\n\tif cfg.Session.EnableAuthTokenHeader {\n\t\texposeHeader = append(exposeHeader, \"X-Auth-Token\")\n\t}\n\n\te.Use(middleware.CORSWithConfig(middleware.CORSConfig{\n\t\tUnsafeWildcardOriginWithAllowCredentials: cfg.Server.Public.Cors.UnsafeWildcardOriginAllowed,\n\t\tAllowOrigins:                             cfg.Server.Public.Cors.AllowOrigins,\n\t\tExposeHeaders:                            exposeHeader,\n\t\tAllowCredentials:                         true,\n\t\t// Based on: Chromium (starting in v76) caps at 2 hours (7200 seconds).\n\t\tMaxAge: 7200,\n\t}))\n\n\tif prometheus != nil {\n\t\te.Use(prometheus)\n\t}\n\n\te.Validator = dto.NewCustomValidator()\n\n\tmailer, err := mail.NewMailer(cfg.EmailDelivery.SMTP)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to create mailer: %w\", err))\n\t}\n\n\tif !cfg.MFA.Enabled && cfg.Password.Enabled {\n\t\tpasswordHandler := NewPasswordHandler(persister, sessionManager, cfg, auditLogger)\n\n\t\tpassword := g.Group(\"/password\")\n\t\tpassword.PUT(\"\", passwordHandler.Set, sessionMiddleware)\n\t\tpassword.POST(\"/login\", passwordHandler.Login)\n\t}\n\n\tuserHandler := NewUserHandler(cfg, persister, sessionManager, auditLogger)\n\tstatusHandler := NewStatusHandler(persister)\n\n\te.GET(\"/\", statusHandler.Status)\n\tg.GET(\"/me\", userHandler.Me, sessionMiddleware)\n\n\tuser := g.Group(\"/users\", webhookMiddleware)\n\tuser.POST(\"\", userHandler.Create)\n\tuser.GET(\"/:id\", userHandler.Get, sessionMiddleware)\n\n\tg.POST(\"/user\", userHandler.GetUserIdByEmail)\n\tg.POST(\"/logout\", userHandler.Logout, sessionMiddleware)\n\n\tif cfg.Account.AllowDeletion {\n\t\tg.DELETE(\"/user\", userHandler.Delete, sessionMiddleware, webhookMiddleware)\n\t}\n\n\thealthHandler := NewHealthHandler()\n\n\thealth := e.Group(\"/health\")\n\thealth.GET(\"/alive\", healthHandler.Alive)\n\thealth.GET(\"/ready\", healthHandler.Ready)\n\n\twellKnownHandler, err := NewWellKnownHandler(*cfg, jwkManager)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to create well-known handler: %w\", err))\n\t}\n\twellKnown := g.Group(\"/.well-known\")\n\twellKnown.GET(\"/jwks.json\", wellKnownHandler.GetPublicKeys)\n\twellKnown.GET(\"/config\", wellKnownHandler.GetConfig)\n\n\temailHandler := NewEmailHandler(cfg, persister, auditLogger)\n\n\tif cfg.Passkey.Enabled {\n\t\twebauthnHandler, err := NewWebauthnHandler(cfg, persister, sessionManager, auditLogger, authenticatorMetadata)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(\"failed to create public webauthn handler: %w\", err))\n\t\t}\n\t\twebauthn := g.Group(\"/webauthn\")\n\t\twebauthnRegistration := webauthn.Group(\"/registration\", sessionMiddleware)\n\t\twebauthnRegistration.POST(\"/initialize\", webauthnHandler.BeginRegistration)\n\t\twebauthnRegistration.POST(\"/finalize\", webauthnHandler.FinishRegistration)\n\n\t\twebauthnLogin := webauthn.Group(\"/login\")\n\t\twebauthnLogin.POST(\"/initialize\", webauthnHandler.BeginAuthentication)\n\t\twebauthnLogin.POST(\"/finalize\", webauthnHandler.FinishAuthentication)\n\n\t\twebauthnCredentials := webauthn.Group(\"/credentials\", sessionMiddleware)\n\t\twebauthnCredentials.GET(\"\", webauthnHandler.ListCredentials)\n\t\twebauthnCredentials.PATCH(\"/:id\", webauthnHandler.UpdateCredential)\n\t\twebauthnCredentials.DELETE(\"/:id\", webauthnHandler.DeleteCredential)\n\t}\n\n\tif !cfg.MFA.Enabled && cfg.Email.Enabled && cfg.Email.UseForAuthentication {\n\t\tpasscodeHandler, err := NewPasscodeHandler(cfg, persister, sessionManager, mailer, auditLogger)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(\"failed to create public passcode handler: %w\", err))\n\t\t}\n\t\tpasscode := g.Group(\"/passcode\")\n\t\tpasscodeLogin := passcode.Group(\"/login\", webhookMiddleware)\n\t\tpasscodeLogin.POST(\"/initialize\", passcodeHandler.Init)\n\t\tpasscodeLogin.POST(\"/finalize\", passcodeHandler.Finish)\n\t}\n\n\temail := g.Group(\"/emails\", sessionMiddleware, webhookMiddleware)\n\temail.GET(\"\", emailHandler.List)\n\temail.POST(\"\", emailHandler.Create)\n\temail.DELETE(\"/:id\", emailHandler.Delete)\n\temail.POST(\"/:id/set_primary\", emailHandler.SetPrimaryEmail)\n\n\tthirdPartyHandler := NewThirdPartyHandler(cfg, persister, sessionManager, auditLogger)\n\tthirdparty := g.Group(\"thirdparty\")\n\tthirdparty.GET(\"/auth\", thirdPartyHandler.Auth)\n\tthirdparty.GET(\"/callback\", thirdPartyHandler.Callback, webhookMiddleware)\n\tthirdparty.POST(\"/callback\", thirdPartyHandler.CallbackPost, webhookMiddleware)\n\n\ttokenHandler := NewTokenHandler(cfg, persister, sessionManager, auditLogger)\n\tg.POST(\"/token\", tokenHandler.Validate)\n\n\tsessionHandler := NewSessionHandler(persister, sessionManager, *cfg)\n\tsessions := g.Group(\"sessions\")\n\tsessions.GET(\"/validate\", sessionHandler.ValidateSession)\n\tsessions.POST(\"/validate\", sessionHandler.ValidateSessionFromBody)\n\n\treturn e\n}\n"
  },
  {
    "path": "backend/handler/session.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\techojwt \"github.com/labstack/echo-jwt/v4\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"net/http\"\n\t\"time\"\n)\n\ntype SessionHandler struct {\n\tpersister      persistence.Persister\n\tsessionManager session.Manager\n\tcfg            config.Config\n}\n\nfunc NewSessionHandler(persister persistence.Persister, sessionManager session.Manager, cfg config.Config) *SessionHandler {\n\treturn &SessionHandler{\n\t\tpersister:      persister,\n\t\tsessionManager: sessionManager,\n\t\tcfg:            cfg,\n\t}\n}\n\nfunc (h *SessionHandler) ValidateSession(c echo.Context) error {\n\tlookup := fmt.Sprintf(\"header:Authorization:Bearer,cookie:%s\", h.cfg.Session.Cookie.GetName())\n\textractors, err := echojwt.CreateExtractors(lookup)\n\n\tif err != nil {\n\t\treturn c.JSON(http.StatusOK, dto.ValidateSessionResponse{IsValid: false})\n\t}\n\n\tfor _, extractor := range extractors {\n\t\tauths, extractorErr := extractor(c)\n\t\tif extractorErr != nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, auth := range auths {\n\t\t\ttoken, tokenErr := h.sessionManager.Verify(auth)\n\t\t\tif tokenErr != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tclaims, err := dto.GetClaimsFromToken(token)\n\t\t\tif err != nil {\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf(\"failed to parse token claims: %w\", err))\n\t\t\t}\n\n\t\t\tsessionModel, err := h.persister.GetSessionPersister().Get(claims.SessionID)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to get session from database: %w\", err)\n\t\t\t}\n\t\t\tif sessionModel == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Update lastUsed field\n\t\t\tsessionModel.LastUsed = time.Now().UTC()\n\t\t\terr = h.persister.GetSessionPersister().Update(*sessionModel)\n\t\t\tif err != nil {\n\t\t\t\treturn dto.ToHttpError(err)\n\t\t\t}\n\n\t\t\treturn c.JSON(http.StatusOK, dto.ValidateSessionResponse{\n\t\t\t\tIsValid:        true,\n\t\t\t\tClaims:         claims,\n\t\t\t\tExpirationTime: &claims.Expiration,\n\t\t\t\tUserID:         &claims.Subject,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn c.JSON(http.StatusOK, dto.ValidateSessionResponse{IsValid: false})\n}\n\nfunc (h *SessionHandler) ValidateSessionFromBody(c echo.Context) error {\n\tvar request dto.ValidateSessionRequest\n\terr := (&echo.DefaultBinder{}).BindBody(c, &request)\n\tif err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\terr = c.Validate(request)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\ttoken, err := h.sessionManager.Verify(request.SessionToken)\n\tif err != nil {\n\t\treturn c.JSON(http.StatusOK, dto.ValidateSessionResponse{IsValid: false})\n\t}\n\n\tclaims, err := dto.GetClaimsFromToken(token)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf(\"failed to parse token claims: %w\", err))\n\t}\n\n\tsessionModel, err := h.persister.GetSessionPersister().Get(claims.SessionID)\n\tif err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif sessionModel == nil {\n\t\treturn c.JSON(http.StatusOK, dto.ValidateSessionResponse{IsValid: false})\n\t}\n\n\t// update lastUsed field\n\tsessionModel.LastUsed = time.Now().UTC()\n\terr = h.persister.GetSessionPersister().Update(*sessionModel)\n\tif err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\treturn c.JSON(http.StatusOK, dto.ValidateSessionResponse{\n\t\tIsValid:        true,\n\t\tClaims:         claims,\n\t\tExpirationTime: &claims.Expiration,\n\t\tUserID:         &claims.Subject,\n\t})\n}\n"
  },
  {
    "path": "backend/handler/session_admin.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/pkg/errors\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\ntype SessionAdminHandler struct {\n\tcfg           *config.Config\n\tpersister     persistence.Persister\n\tsessionManger session.Manager\n\tauditLogger   auditlog.Logger\n}\n\nfunc NewSessionAdminHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) SessionAdminHandler {\n\treturn SessionAdminHandler{\n\t\tcfg:           cfg,\n\t\tpersister:     persister,\n\t\tsessionManger: sessionManager,\n\t\tauditLogger:   auditLogger,\n\t}\n}\n\nfunc (h *SessionAdminHandler) Generate(ctx echo.Context) error {\n\tvar body admin.CreateSessionTokenDto\n\tif err := (&echo.DefaultBinder{}).BindBody(ctx, &body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := ctx.Validate(body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tuserID, err := uuid.FromString(body.UserID)\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse userId as uuid\").SetInternal(err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\")\n\t}\n\n\tencodedToken, rawToken, err := h.sessionManger.GenerateJWT(dto.UserJWTFromUserModel(user))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to generate JWT: %w\", err)\n\t}\n\n\tactiveSessions, err := h.persister.GetSessionPersister().ListActive(userID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list active sessions: %w\", err)\n\t}\n\n\t// remove all server side sessions that exceed the limit\n\tif len(activeSessions) >= h.cfg.Session.Limit {\n\t\tfor i := h.cfg.Session.Limit - 1; i < len(activeSessions); i++ {\n\t\t\terr = h.persister.GetSessionPersister().Delete(activeSessions[i])\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to remove latest session: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tsessionID, _ := rawToken.Get(\"session_id\")\n\n\texpirationTime := rawToken.Expiration()\n\tsessionModel := models.Session{\n\t\tID:        uuid.FromStringOrNil(sessionID.(string)),\n\t\tUserID:    userID,\n\t\tCreatedAt: rawToken.IssuedAt(),\n\t\tUpdatedAt: rawToken.IssuedAt(),\n\t\tExpiresAt: &expirationTime,\n\t\tLastUsed:  rawToken.IssuedAt(),\n\t}\n\n\tif len(body.UserAgent) > 0 {\n\t\tsessionModel.UserAgent = nulls.NewString(body.UserAgent)\n\t}\n\n\tif len(body.IpAddress) > 0 {\n\t\tsessionModel.IpAddress = nulls.NewString(body.IpAddress)\n\t}\n\n\terr = h.persister.GetSessionPersister().Create(sessionModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store session: %w\", err)\n\t}\n\n\tresponse := admin.CreateSessionTokenResponse{\n\t\tSessionToken: encodedToken,\n\t}\n\n\terr = h.auditLogger.Create(ctx, models.AuditLogLoginSuccess, user, nil, auditlog.Detail(\"api\", \"admin\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\treturn ctx.JSON(http.StatusOK, response)\n}\n\nfunc (h *SessionAdminHandler) List(ctx echo.Context) error {\n\tlistDto, err := loadDto[admin.ListSessionsRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(listDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tsessions, err := h.persister.GetSessionPersister().ListActive(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.JSON(http.StatusOK, sessions)\n}\n\nfunc (h *SessionAdminHandler) Delete(ctx echo.Context) error {\n\tdeleteDto, err := loadDto[admin.DeleteSessionRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(deleteDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tsessionID, err := uuid.FromString(deleteDto.SessionID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse session_id as uuid: %s\", err)\n\t}\n\n\tsessionModel, err := h.persister.GetSessionPersister().Get(sessionID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif sessionModel == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t} else if sessionModel.UserID != userID {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"session does not belong to user\"))\n\t}\n\n\terr = h.persister.GetSessionPersister().Delete(*sessionModel)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.NoContent(http.StatusNoContent)\n}\n"
  },
  {
    "path": "backend/handler/session_admin_test.go",
    "content": "package handler\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestSessionAdminSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(sessionAdminSuite))\n}\n\ntype sessionAdminSuite struct {\n\ttest.Suite\n}\n\nfunc (s *sessionAdminSuite) TestSessionAdminHandler_List() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/sessions\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserID             string\n\t\texpectedStatusCode int\n\t\texpectedCount      int\n\t}{\n\t\t{\n\t\t\tname:               \"should return a list of sessions with multiple entries\",\n\t\t\tuserID:             \"ec4ef049-5b88-4321-a173-21b0eff06a04\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedCount:      2,\n\t\t},\n\t\t{\n\t\t\tname:               \"should return a list of sessions with one entry\",\n\t\t\tuserID:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedCount:      1,\n\t\t},\n\t\t{\n\t\t\tname:               \"should return an empty list\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\texpectedCount:      0,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserID:             \"customUserId\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedCount:      0,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserID:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t\texpectedCount:      0,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserID:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t\texpectedCount:      0,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s/sessions\", currentTest.userID), nil)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar sessions []admin.ListSessionsRequestDto\n\t\t\t\terr = json.Unmarshal(rec.Body.Bytes(), &sessions)\n\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\ts.Equal(currentTest.expectedCount, len(sessions))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *sessionAdminSuite) TestSessionAdminHandler_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/sessions\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserID             string\n\t\tsessionID          string\n\t\texpectedStatusCode int\n\t\texpectedCount      int\n\t}{\n\t\t{\n\t\t\tname:               \"should delete session for user with multiple sessions\",\n\t\t\tuserID:             \"ec4ef049-5b88-4321-a173-21b0eff06a04\",\n\t\t\tsessionID:          \"d8d6dc27-fcf9-4a5c-bb50-a7a03067d936\",\n\t\t\texpectedCount:      1,\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should delete session for user with one session\",\n\t\t\tuserID:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\tsessionID:          \"108f3789-a795-43bd-a58f-ac8e80a213cd\",\n\t\t\texpectedCount:      0,\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if session is not found\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tsessionID:          \"649c95d7-9840-4e6d-be00-6c6b93c9e885\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if session is not associated to the user\",\n\t\t\tuserID:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\tsessionID:          \"74ba812a-923a-43e4-8020-9535dcadc0a8\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserID:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\tsessionID:          \"6e405e60-f70c-4b8a-b0d5-8ba05dd3e793\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserID:             \"\",\n\t\t\tsessionID:          \"6e405e60-f70c-4b8a-b0d5-8ba05dd3e793\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty sessionID\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tsessionID:          \"\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserID:             \"customUserId\",\n\t\t\tsessionID:          \"d8d6dc27-fcf9-4a5c-bb50-a7a03067d936\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s/sessions/%s\", currentTest.userID, currentTest.sessionID), nil)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\tif http.StatusNoContent == rec.Code {\n\t\t\t\tcredentials, err := s.Storage.GetSessionPersister().ListActive(uuid.FromStringOrNil(currentTest.userID))\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Equal(currentTest.expectedCount, len(credentials))\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/status.go",
    "content": "package handler\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"net/http\"\n)\n\ntype StatusHandler struct {\n\tpersister persistence.Persister\n}\n\nfunc NewStatusHandler(persister persistence.Persister) *StatusHandler {\n\treturn &StatusHandler{\n\t\tpersister: persister,\n\t}\n}\n\nfunc (h *StatusHandler) Status(c echo.Context) error {\n\t// random query to check DB connectivity\n\t_, err := h.persister.GetJwkPersister().GetAll()\n\tif err != nil {\n\t\treturn c.Render(http.StatusInternalServerError, \"status\", map[string]bool{\"dbError\": true})\n\t}\n\n\treturn c.Render(http.StatusOK, \"status\", nil)\n}\n"
  },
  {
    "path": "backend/handler/thirdparty.go",
    "content": "package handler\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n\twebhookUtils \"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n\t\"golang.org/x/oauth2\"\n)\n\ntype ThirdPartyHandler struct {\n\tauditLogger    auditlog.Logger\n\tcfg            *config.Config\n\tpersister      persistence.Persister\n\tsessionManager session.Manager\n}\n\nfunc NewThirdPartyHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) *ThirdPartyHandler {\n\treturn &ThirdPartyHandler{\n\t\tauditLogger:    auditLogger,\n\t\tcfg:            cfg,\n\t\tpersister:      persister,\n\t\tsessionManager: sessionManager,\n\t}\n}\n\nfunc (h *ThirdPartyHandler) Auth(c echo.Context) error {\n\terrorRedirectTo := c.Request().Header.Get(\"Referer\")\n\tif errorRedirectTo == \"\" {\n\t\terrorRedirectTo = h.cfg.ThirdParty.ErrorRedirectURL\n\t}\n\n\tvar request dto.ThirdPartyAuthRequest\n\terr := c.Bind(&request)\n\tif err != nil {\n\t\treturn h.redirectError(c, thirdparty.ErrorServer(\"could not decode request payload\").WithCause(err), errorRedirectTo)\n\t}\n\n\terr = c.Validate(request)\n\tif err != nil {\n\t\treturn h.redirectError(c, thirdparty.ErrorInvalidRequest(err.Error()).WithCause(err), errorRedirectTo)\n\t}\n\n\tif ok := thirdparty.IsAllowedRedirect(h.cfg.ThirdParty, request.RedirectTo); !ok {\n\t\treturn h.redirectError(c, thirdparty.ErrorInvalidRequest(fmt.Sprintf(\"redirect to '%s' not allowed\", request.RedirectTo)), errorRedirectTo)\n\t}\n\n\tprovider, err := thirdparty.GetProvider(h.cfg.ThirdParty, request.Provider)\n\tif err != nil {\n\t\treturn h.redirectError(c, thirdparty.ErrorInvalidRequest(err.Error()).WithCause(err), errorRedirectTo)\n\t}\n\n\tstate, err := thirdparty.GenerateState(h.cfg, provider.ID(), request.RedirectTo)\n\tif err != nil {\n\t\treturn h.redirectError(c, thirdparty.ErrorServer(\"could not generate state\").WithCause(err), errorRedirectTo)\n\t}\n\n\tauthCodeUrl := provider.AuthCodeURL(string(state))\n\n\tcookie := utils.GenerateStateCookie(h.cfg, utils.HankoThirdpartyStateCookie, string(state), utils.CookieOptions{\n\t\tMaxAge:   300,\n\t\tPath:     \"/\",\n\t\tSameSite: http.SameSiteLaxMode,\n\t})\n\n\tc.SetCookie(cookie)\n\n\treturn c.Redirect(http.StatusTemporaryRedirect, authCodeUrl)\n}\n\nfunc (h *ThirdPartyHandler) CallbackPost(c echo.Context) error {\n\tq, err := c.FormParams()\n\tif err != nil {\n\t\treturn h.redirectError(c, thirdparty.ErrorServer(\"could not get form parameters\"), h.cfg.ThirdParty.ErrorRedirectURL)\n\t}\n\treturn c.Redirect(http.StatusSeeOther, fmt.Sprintf(\"/thirdparty/callback?%s\", q.Encode()))\n}\n\nfunc (h *ThirdPartyHandler) Callback(c echo.Context) error {\n\tvar redirectToURL *url.URL\n\tvar accountLinkingResult *thirdparty.AccountLinkingResult\n\terr := h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tvar callback dto.ThirdPartyAuthCallback\n\t\tterr := c.Bind(&callback)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorServer(\"could not decode request payload\").WithCause(terr)\n\t\t}\n\n\t\tterr = c.Validate(callback)\n\t\tif terr != nil {\n\t\t\tif eerr, ok := terr.(*echo.HTTPError); ok {\n\t\t\t\tif message, ok2 := eerr.Message.(string); ok2 {\n\t\t\t\t\treturn thirdparty.ErrorInvalidRequest(message).WithCause(terr)\n\t\t\t\t} else {\n\t\t\t\t\treturn thirdparty.ErrorInvalidRequest(terr.Error()).WithCause(terr)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn thirdparty.ErrorInvalidRequest(terr.Error()).WithCause(terr)\n\t\t\t}\n\t\t}\n\n\t\texpectedStateCookie, terr := c.Cookie(utils.HankoThirdpartyStateCookie)\n\t\tif terr != nil && !errors.Is(terr, http.ErrNoCookie) {\n\t\t\treturn thirdparty.ErrorInvalidRequest(\"could not read state cookie\").WithCause(terr)\n\t\t}\n\n\t\tvar expectedState string\n\t\tif expectedStateCookie != nil {\n\t\t\texpectedState = expectedStateCookie.Value\n\t\t}\n\t\tvar state *thirdparty.State\n\t\tstate, terr = thirdparty.VerifyState(h.cfg, callback.State, expectedState)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorInvalidRequest(terr.Error()).WithCause(terr)\n\t\t}\n\n\t\tredirectToURL, terr = url.Parse(state.RedirectTo)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorServer(\"could not parse redirect url\").WithCause(terr)\n\t\t}\n\n\t\tif callback.HasError() {\n\t\t\treturn thirdparty.NewThirdPartyError(callback.Error, callback.ErrorDescription)\n\t\t}\n\n\t\tprovider, terr := thirdparty.GetProvider(h.cfg.ThirdParty, state.Provider)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorInvalidRequest(terr.Error()).WithCause(terr)\n\t\t}\n\n\t\tif callback.AuthCode == \"\" {\n\t\t\treturn thirdparty.ErrorInvalidRequest(\"auth code missing from request\")\n\t\t}\n\n\t\topts := []oauth2.AuthCodeOption{}\n\t\tif state.CodeVerifier != \"\" && provider.ID() != \"linkedin\" {\n\t\t\topts = append(opts, oauth2.VerifierOption(state.CodeVerifier))\n\t\t}\n\t\toAuthToken, terr := provider.GetOAuthToken(callback.AuthCode, opts...)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorInvalidRequest(\"could not exchange authorization code for access token\").WithCause(terr)\n\t\t}\n\n\t\tuserData, terr := provider.GetUserData(oAuthToken)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorInvalidRequest(\"could not retrieve user data from provider\").WithCause(terr)\n\t\t}\n\n\t\tlinkingResult, terr := thirdparty.LinkAccount(tx, h.cfg, h.persister, userData, provider.ID(), false, nil, state.IsFlow, state.UserID)\n\t\tif terr != nil {\n\t\t\treturn terr\n\t\t}\n\t\taccountLinkingResult = linkingResult\n\n\t\tidentityModel, err := h.persister.GetIdentityPersisterWithConnection(tx).Get(userData.Metadata.Subject, provider.ID())\n\t\tif err != nil {\n\t\t\treturn thirdparty.ErrorServer(\"could not get identity\").WithCause(err)\n\t\t}\n\n\t\tif identityModel != nil && state.UserID != nil && identityModel.UserID != nil && *identityModel.UserID != *state.UserID {\n\t\t\treturn thirdparty.ErrorInvalidRequest(\"identity already exists for a different user\")\n\t\t}\n\n\t\ttokenOpts := []func(*models.Token){\n\t\t\tmodels.TokenForFlowAPI(state.IsFlow),\n\t\t\tmodels.TokenWithIdentityID(identityModel.ID),\n\t\t\tmodels.TokenUserCreated(linkingResult.UserCreated),\n\t\t\tmodels.TokenWithLinkUser(state.UserID != nil),\n\t\t}\n\t\tif state.CodeVerifier != \"\" {\n\t\t\ttokenOpts = append(tokenOpts, models.TokenPKCESessionVerifier(state.CodeVerifier))\n\t\t}\n\t\ttoken, terr := models.NewToken(\n\t\t\tlinkingResult.User.ID,\n\t\t\ttokenOpts...,\n\t\t)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorServer(\"could not create token\").WithCause(terr)\n\t\t}\n\n\t\tterr = h.persister.GetTokenPersisterWithConnection(tx).Create(*token)\n\t\tif terr != nil {\n\t\t\treturn thirdparty.ErrorServer(\"could not save token to db\").WithCause(terr)\n\t\t}\n\n\t\tquery := redirectToURL.Query()\n\t\tquery.Add(utils.HankoTokenQuery, token.Value)\n\t\tredirectToURL.RawQuery = query.Encode()\n\n\t\tc.SetCookie(&http.Cookie{\n\t\t\tName:     utils.HankoThirdpartyStateCookie,\n\t\t\tValue:    \"\",\n\t\t\tPath:     \"/\",\n\t\t\tDomain:   h.cfg.Session.Cookie.Domain,\n\t\t\tMaxAge:   -1,\n\t\t\tSecure:   h.cfg.Session.Cookie.Secure,\n\t\t\tHttpOnly: h.cfg.Session.Cookie.HttpOnly,\n\t\t\tSameSite: http.SameSiteLaxMode,\n\t\t})\n\n\t\treturn nil\n\t})\n\n\terrorRedirect := h.cfg.ThirdParty.ErrorRedirectURL\n\tif redirectToURL != nil {\n\t\terrorRedirect = redirectToURL.String()\n\t}\n\n\tif err != nil {\n\t\treturn h.redirectError(c, err, errorRedirect)\n\t}\n\n\terr = h.auditLogger.Create(c, accountLinkingResult.Type, accountLinkingResult.User, nil)\n\tif err != nil {\n\t\treturn h.redirectError(c, thirdparty.ErrorServer(\"could not create audit log\").WithCause(err), errorRedirect)\n\t}\n\n\tif accountLinkingResult.WebhookEvent != nil {\n\t\terr = webhookUtils.TriggerWebhooks(c, h.persister.GetConnection(), *accountLinkingResult.WebhookEvent, admin.FromUserModel(*accountLinkingResult.User))\n\t\tif err != nil {\n\t\t\tc.Logger().Warn(err)\n\t\t}\n\t}\n\n\treturn c.Redirect(http.StatusTemporaryRedirect, redirectToURL.String())\n}\n\nfunc (h *ThirdPartyHandler) redirectError(c echo.Context, error error, to string) error {\n\tredirectTo := h.cfg.ThirdParty.ErrorRedirectURL\n\tif to != \"\" {\n\t\tredirectTo = to\n\t}\n\n\terr := h.auditError(c, error)\n\tif err != nil {\n\t\terror = err\n\t}\n\n\tredirectURL := thirdparty.GetErrorUrl(redirectTo, error)\n\treturn c.Redirect(http.StatusTemporaryRedirect, redirectURL)\n}\n\nfunc (h *ThirdPartyHandler) auditError(c echo.Context, err error) error {\n\te, ok := err.(*thirdparty.ThirdPartyError)\n\n\tvar auditLogError error\n\tif ok && e.Code != thirdparty.ErrorCodeServerError {\n\t\tauditLogError = h.auditLogger.Create(c, models.AuditLogThirdPartySignInSignUpFailed, nil, err)\n\t}\n\treturn auditLogError\n}\n"
  },
  {
    "path": "backend/handler/thirdparty_auth_test.go",
    "content": "package handler\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n)\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Auth() {\n\ttests := []struct {\n\t\tname                     string\n\t\treferer                  string\n\t\tenabledProviders         []string\n\t\tallowedRedirectURLs      []string\n\t\trequestedProvider        string\n\t\trequestedRedirectTo      string\n\t\texpectedBaseURL          string\n\t\texpectedError            string\n\t\texpectedErrorDescription string // can be a partial message\n\t}{\n\t\t{\n\t\t\tname:                \"successful redirect to google\",\n\t\t\treferer:             \"https://login.test.example\",\n\t\t\tenabledProviders:    []string{\"google\"},\n\t\t\tallowedRedirectURLs: []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:   \"google\",\n\t\t\trequestedRedirectTo: \"https://app.test.example\",\n\t\t\texpectedBaseURL:     thirdparty.GoogleOauthAuthEndpoint,\n\t\t},\n\t\t{\n\t\t\tname:                \"successful redirect to github\",\n\t\t\treferer:             \"https://login.test.example\",\n\t\t\tenabledProviders:    []string{\"github\"},\n\t\t\tallowedRedirectURLs: []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:   \"github\",\n\t\t\trequestedRedirectTo: \"https://app.test.example\",\n\t\t\texpectedBaseURL:     thirdparty.GithubOauthAuthEndpoint,\n\t\t},\n\t\t{\n\t\t\tname:                \"successful redirect to apple\",\n\t\t\treferer:             \"https://login.test.example\",\n\t\t\tenabledProviders:    []string{\"apple\"},\n\t\t\tallowedRedirectURLs: []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:   \"apple\",\n\t\t\trequestedRedirectTo: \"https://app.test.example\",\n\t\t\texpectedBaseURL:     thirdparty.AppleAuthEndpoint,\n\t\t},\n\t\t{\n\t\t\tname:                \"successful redirect to discord\",\n\t\t\treferer:             \"https://login.test.example\",\n\t\t\tenabledProviders:    []string{\"discord\"},\n\t\t\tallowedRedirectURLs: []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:   \"discord\",\n\t\t\trequestedRedirectTo: \"https://app.test.example\",\n\t\t\texpectedBaseURL:     thirdparty.DiscordOauthAuthEndpoint,\n\t\t},\n\t\t{\n\t\t\tname:                \"successful redirect to microsoft\",\n\t\t\treferer:             \"https://login.test.example\",\n\t\t\tenabledProviders:    []string{\"microsoft\"},\n\t\t\tallowedRedirectURLs: []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:   \"microsoft\",\n\t\t\trequestedRedirectTo: \"https://app.test.example\",\n\t\t\texpectedBaseURL:     thirdparty.MicrosoftOAuthAuthEndpoint,\n\t\t},\n\t\t{\n\t\t\tname:                \"successful redirect to facebook\",\n\t\t\treferer:             \"https://login.test.example\",\n\t\t\tenabledProviders:    []string{\"facebook\"},\n\t\t\tallowedRedirectURLs: []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:   \"facebook\",\n\t\t\trequestedRedirectTo: \"https://app.test.example\",\n\t\t\texpectedBaseURL:     thirdparty.FacebookOauthAuthEndpoint,\n\t\t},\n\t\t{\n\t\t\tname:                     \"error redirect on missing provider\",\n\t\t\treferer:                  \"https://login.test.example\",\n\t\t\trequestedRedirectTo:      \"https://app.test.example\",\n\t\t\texpectedBaseURL:          \"https://login.test.example\",\n\t\t\texpectedError:            thirdparty.ErrorCodeInvalidRequest,\n\t\t\texpectedErrorDescription: \"is a required field\",\n\t\t},\n\t\t{\n\t\t\tname:                     \"error redirect on missing redirectTo\",\n\t\t\treferer:                  \"https://login.test.example\",\n\t\t\trequestedProvider:        \"google\",\n\t\t\texpectedBaseURL:          \"https://login.test.example\",\n\t\t\texpectedError:            thirdparty.ErrorCodeInvalidRequest,\n\t\t\texpectedErrorDescription: \"is a required field\",\n\t\t},\n\t\t{\n\t\t\tname:                     \"error redirect when requested provider is disabled\",\n\t\t\treferer:                  \"https://login.test.example\",\n\t\t\tenabledProviders:         []string{\"github\"},\n\t\t\tallowedRedirectURLs:      []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:        \"google\",\n\t\t\trequestedRedirectTo:      \"https://app.test.example\",\n\t\t\texpectedBaseURL:          \"https://login.test.example\",\n\t\t\texpectedError:            thirdparty.ErrorCodeInvalidRequest,\n\t\t\texpectedErrorDescription: \"provider is disabled\",\n\t\t},\n\t\t{\n\t\t\tname:                     \"error redirect when requesting an unknown provider\",\n\t\t\treferer:                  \"https://login.test.example\",\n\t\t\tallowedRedirectURLs:      []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:        \"unknownProvider\",\n\t\t\trequestedRedirectTo:      \"https://app.test.example\",\n\t\t\texpectedBaseURL:          \"https://login.test.example\",\n\t\t\texpectedError:            thirdparty.ErrorCodeInvalidRequest,\n\t\t\texpectedErrorDescription: \"unknown provider\",\n\t\t},\n\t\t{\n\t\t\tname:                     \"error redirect when requesting a redirectTo that is not allowed\",\n\t\t\treferer:                  \"https://login.test.example\",\n\t\t\tenabledProviders:         []string{\"google\"},\n\t\t\tallowedRedirectURLs:      []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:        \"google\",\n\t\t\trequestedRedirectTo:      \"https://app.test.wrong\",\n\t\t\texpectedBaseURL:          \"https://login.test.example\",\n\t\t\texpectedError:            thirdparty.ErrorCodeInvalidRequest,\n\t\t\texpectedErrorDescription: \"redirect to 'https://app.test.wrong' not allowed\",\n\t\t},\n\t\t{\n\t\t\tname:                     \"error redirect with redirect to error redirect url if referer not present\",\n\t\t\tallowedRedirectURLs:      []string{\"https://*.test.example\"},\n\t\t\trequestedProvider:        \"unknownProvider\",\n\t\t\trequestedRedirectTo:      \"https://app.test.example\",\n\t\t\texpectedBaseURL:          \"https://error.test.example\",\n\t\t\texpectedError:            thirdparty.ErrorCodeInvalidRequest,\n\t\t\texpectedErrorDescription: \"unknown provider\",\n\t\t},\n\t}\n\n\tfor _, testData := range tests {\n\t\ts.Run(testData.name, func() {\n\t\t\tcfg := s.setUpConfig(testData.enabledProviders, testData.allowedRedirectURLs)\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, \"/thirdparty/auth\", nil)\n\n\t\t\tparams := url.Values{}\n\t\t\tif testData.requestedProvider != \"\" {\n\t\t\t\tparams.Add(\"provider\", testData.requestedProvider)\n\t\t\t}\n\t\t\tif testData.requestedRedirectTo != \"\" {\n\t\t\t\tparams.Add(\"redirect_to\", testData.requestedRedirectTo)\n\t\t\t}\n\t\t\treq.URL.RawQuery = params.Encode()\n\n\t\t\treq.Header.Set(\"Referer\", testData.referer)\n\n\t\t\tc, rec := s.setUpContext(req)\n\t\t\thandler := s.setUpHandler(cfg)\n\n\t\t\terr := handler.Auth(c)\n\t\t\ts.NoError(err)\n\n\t\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\t\tu, err := url.Parse(rec.Header().Get(\"Location\"))\n\t\t\ts.NoError(err)\n\n\t\t\ts.Equal(testData.expectedBaseURL, u.Scheme+\"://\"+u.Host+u.Path)\n\n\t\t\tq := u.Query()\n\n\t\t\tif testData.expectedError != \"\" {\n\t\t\t\ts.Equal(testData.expectedError, q.Get(\"error\"))\n\t\t\t\terrorDescription := q.Get(\"error_description\")\n\t\t\t\tisCorrectErrorDescription := strings.Contains(errorDescription, testData.expectedErrorDescription)\n\t\t\t\ts.Truef(isCorrectErrorDescription, \"error description '%s' does not contain '%s'\", errorDescription, testData.expectedErrorDescription)\n\t\t\t} else {\n\t\t\t\ts.Equal(cfg.ThirdParty.RedirectURL, q.Get(\"redirect_uri\"))\n\t\t\t\ts.Equal(cfg.ThirdParty.Providers.Get(testData.requestedProvider).ClientID, q.Get(\"client_id\"))\n\t\t\t\ts.Equal(\"code\", q.Get(\"response_type\"))\n\n\t\t\t\texpectedState := rec.Result().Cookies()[0].Value\n\t\t\t\tstate, err := thirdparty.VerifyState(cfg, q.Get(\"state\"), expectedState)\n\t\t\t\ts.NoError(err)\n\n\t\t\t\ts.Equal(strings.ToLower(testData.requestedProvider), state.Provider)\n\n\t\t\t\tif testData.requestedRedirectTo == \"\" {\n\t\t\t\t\ts.Equal(cfg.ThirdParty.ErrorRedirectURL, state.RedirectTo)\n\t\t\t\t} else {\n\t\t\t\t\ts.Equal(testData.requestedRedirectTo, state.RedirectTo)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/thirdparty_callback_error_test.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/h2non/gock\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n)\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_LinkingNotAllowedForProvider() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_email_already_exists\",\n\t\t\tEmail:         \"test-no-identity@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\tcfg.ThirdParty.Providers.Google.AllowLinking = false\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeUserConflict, location.Query().Get(\"error\"))\n\t\ts.Equal(\"third party account linking for existing user with same email disallowed\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_SignInMultipleAccounts() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_abcde\",\n\t\t\tEmail:         \"provider-primary-email-changed@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeMultipleAccounts, location.Query().Get(\"error\"))\n\t\ts.Equal(fmt.Sprintf(\"cannot identify associated user: '%s' is used by multiple accounts\", \"provider-primary-email-changed@example.com\"), location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_NoState() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\treq := httptest.NewRequest(http.MethodGet, \"/thirdparty/callback?code=abcde\", nil)\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeInvalidRequest, location.Query().Get(\"error\"))\n\t\ts.Equal(\"State is a required field\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_StateMismatch() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\tmismatchedState, err := thirdparty.GenerateState(cfg, \"github\", \"https://foo.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(mismatchedState),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeInvalidRequest, location.Query().Get(\"error\"))\n\t\ts.Equal(\"could not verify state\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_NoThirdPartyCookie() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeInvalidRequest, location.Query().Get(\"error\"))\n\t\ts.Equal(\"expected state must not be empty\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_ProviderError() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\tproviderError := \"access_denied\"\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s&error=%s\", state, providerError), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(providerError, location.Query().Get(\"error\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_ProviderDisabled() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := s.setUpConfig([]string{\"github\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeInvalidRequest, location.Query().Get(\"error\"))\n\t\ts.Equal(\"google provider is disabled\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_NoAuthCode() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeInvalidRequest, location.Query().Get(\"error\"))\n\t\ts.Equal(\"auth code missing from request\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_OAuthTokenExchange() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(400).\n\t\tJSON(map[string]string{\"error\": \"incorrect_client_credentials\"})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeInvalidRequest, location.Query().Get(\"error\"))\n\t\ts.Equal(\"could not exchange authorization code for access token\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_VerificationRequiredUnverifiedProviderEmail() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_abcde\",\n\t\t\tEmail:         \"test-google-signup@example.com\",\n\t\t\tEmailVerified: false,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\tcfg.Email.RequireVerification = true\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeUnverifiedProviderEmail, location.Query().Get(\"error\"))\n\t\ts.Equal(\"third party provider email must be verified\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_MicrosoftUnverifiedEmail() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tfakeIdToken := s.setUpMicrosoftIdToken(\"microsoft_abcde\", \"fakeClientID\", \"test-with-microsoft-identity@example.com\", false)\n\tgock.New(thirdparty.MicrosoftOAuthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\", \"id_token\": fakeIdToken})\n\n\tfakeJwkSet := s.setUpFakeJwkSet()\n\tgock.New(thirdparty.MicrosoftKeysEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(fakeJwkSet)\n\n\tcfg := s.setUpConfig([]string{\"microsoft\"}, []string{\"https://example.com\"})\n\tcfg.Emails.RequireVerification = true\n\n\tstate, err := thirdparty.GenerateState(cfg, \"microsoft\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\t\tlocation, err := rec.Result().Location()\n\t\ts.NoError(err)\n\n\t\ts.Equal(thirdparty.ErrorCodeUnverifiedProviderEmail, location.Query().Get(\"error\"))\n\t\ts.Equal(\"third party provider email must be verified\", location.Query().Get(\"error_description\"))\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_signup_failed\"}, \"\", \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n"
  },
  {
    "path": "backend/handler/thirdparty_callback_test.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/h2non/gock\"\n\t\"github.com/teamhanko/hanko/backend/v2/thirdparty\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n)\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Google() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_abcde\",\n\t\t\tEmail:         \"test-google-signup@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-google-signup@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signup_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Google() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_abcde\",\n\t\t\tEmail:         \"test-with-google-identity@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-google-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_GitHub() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tgock.New(thirdparty.GithubOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GithubUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GithubUser{\n\t\t\tID:   1234,\n\t\t\tName: \"John Doe\",\n\t\t})\n\n\tgock.New(thirdparty.GitHubEmailsEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON([]*thirdparty.GithubUserEmail{\n\t\t\t{\n\t\t\t\tEmail:    \"test-github-signup@example.com\",\n\t\t\t\tPrimary:  true,\n\t\t\t\tVerified: true,\n\t\t\t},\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"github\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"github\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-github-signup@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"github\", \"1234\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signup_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_GitHub() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GithubOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GithubUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GithubUser{\n\t\t\tID:   1234,\n\t\t\tName: \"John Doe\",\n\t\t})\n\n\tgock.New(thirdparty.GitHubEmailsEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON([]*thirdparty.GithubUserEmail{\n\t\t\t{\n\t\t\t\tEmail:    \"test-with-github-identity@example.com\",\n\t\t\t\tPrimary:  true,\n\t\t\t\tVerified: true,\n\t\t\t},\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"github\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"github\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-github-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"github\", \"1234\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Apple() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tfakeIdToken := s.setUpAppleIdToken(\"apple_abcde\", \"fakeClientID\", \"test-apple-signup@example.com\", true, false)\n\tgock.New(thirdparty.AppleTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\", \"id_token\": fakeIdToken})\n\n\tfakeJwkSet := s.setUpFakeJwkSet()\n\tgock.New(thirdparty.AppleKeysEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(fakeJwkSet)\n\n\tcfg := s.setUpConfig([]string{\"apple\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"apple\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-apple-signup@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"apple\", \"apple_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signup_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Apple() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tfakeIdToken := s.setUpAppleIdToken(\"apple_abcde\", \"fakeClientID\", \"test-with-apple-identity@example.com\", true, false)\n\tgock.New(thirdparty.AppleTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\", \"id_token\": fakeIdToken})\n\n\tfakeJwkSet := s.setUpFakeJwkSet()\n\tgock.New(thirdparty.AppleKeysEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(fakeJwkSet)\n\n\tcfg := s.setUpConfig([]string{\"apple\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"apple\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-apple-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"apple\", \"apple_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Apple_WithBooleanEmailVerifiedClaim() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tfakeIdToken := s.setUpAppleIdToken(\"apple_abcde\", \"fakeClientID\", \"test-with-apple-identity@example.com\", true, true)\n\tgock.New(thirdparty.AppleTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\", \"id_token\": fakeIdToken})\n\n\tfakeJwkSet := s.setUpFakeJwkSet()\n\tgock.New(thirdparty.AppleKeysEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(fakeJwkSet)\n\n\tcfg := s.setUpConfig([]string{\"apple\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"apple\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-apple-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"apple\", \"apple_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Discord() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tgock.New(thirdparty.DiscordOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.DiscordUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.DiscordUser{\n\t\t\tID:       \"discord_abcde\",\n\t\t\tEmail:    \"test-discord-signup@example.com\",\n\t\t\tVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"discord\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"discord\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-discord-signup@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"discord\", \"discord_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signup_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Discord() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.DiscordOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.DiscordUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.DiscordUser{\n\t\t\tID:       \"discord_abcde\",\n\t\t\tEmail:    \"test-with-discord-identity@example.com\",\n\t\t\tVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"discord\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"discord\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-discord-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"discord\", \"discord_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Microsoft() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tfakeIdToken := s.setUpMicrosoftIdToken(\"microsoft_abcde\", \"fakeClientID\", \"test-microsoft-signup@example.com\", true)\n\tgock.New(thirdparty.MicrosoftOAuthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\", \"id_token\": fakeIdToken})\n\n\tfakeJwkSet := s.setUpFakeJwkSet()\n\tgock.New(thirdparty.MicrosoftKeysEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(fakeJwkSet)\n\n\tcfg := s.setUpConfig([]string{\"microsoft\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"microsoft\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-microsoft-signup@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"microsoft\", \"microsoft_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signup_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Microsoft() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tfakeIdToken := s.setUpMicrosoftIdToken(\"microsoft_abcde\", \"fakeClientID\", \"test-with-microsoft-identity@example.com\", true)\n\tgock.New(thirdparty.MicrosoftOAuthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\", \"id_token\": fakeIdToken})\n\n\tfakeJwkSet := s.setUpFakeJwkSet()\n\tgock.New(thirdparty.MicrosoftKeysEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(fakeJwkSet)\n\n\tcfg := s.setUpConfig([]string{\"microsoft\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"microsoft\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-microsoft-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"microsoft\", \"microsoft_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Facebook() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tgock.New(thirdparty.FacebookOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.FacebookUserInfoEndpoint).\n\t\tGet(\"/me\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.FacebookUser{\n\t\t\tID:    \"facebook_abcde\",\n\t\t\tEmail: \"test-facebook-signup@example.com\",\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"facebook\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"facebook\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-facebook-signup@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"facebook\", \"facebook_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signup_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Facebook() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.FacebookOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.FacebookUserInfoEndpoint).\n\t\tGet(\"/me\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.FacebookUser{\n\t\t\tID:    \"facebook_abcde\",\n\t\t\tEmail: \"test-with-facebook-identity@example.com\",\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"facebook\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"facebook\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-facebook-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"facebook\", \"facebook_abcde\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), \"\", \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_WithUnclaimedEmail() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_unclaimed_email\",\n\t\t\tEmail:         \"unclaimed-email@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"unclaimed-email@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.True(email.IsPrimary())\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_unclaimed_email\")\n\t\ts.NotNil(identity)\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signup_succeeded\"}, user.ID.String(), email.Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_ProviderEMailChangedToExistingEmail() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_abcde\",\n\t\t\tEmail:         \"test-with-google-identity-changed@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-google-identity-changed@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_abcde\")\n\t\ts.NotNil(identity)\n\t\ts.Equal(\"test-with-google-identity-changed@example.com\", identity.Data[\"email\"])\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), user.Emails.GetPrimary().Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_ProviderEMailChangedToUnclaimedEmail() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_abcde\",\n\t\t\tEmail:         \"unclaimed-email@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"unclaimed-email@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_abcde\")\n\t\ts.NotNil(identity)\n\t\ts.Equal(\"unclaimed-email@example.com\", identity.Data[\"email\"])\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), user.Emails.GetPrimary().Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_ProviderEMailChangedToNonExistentEmail() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_abcde\",\n\t\t\tEmail:         \"non-existent-email@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"non-existent-email@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\t\ts.Len(user.Emails, 3)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_abcde\")\n\t\ts.NotNil(identity)\n\t\ts.Equal(\"non-existent-email@example.com\", identity.Data[\"email\"])\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_signin_succeeded\"}, user.ID.String(), user.Emails.GetPrimary().Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Link_ExistingAccountNoIdentities() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_1234\",\n\t\t\tEmail:         \"test-no-identity@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-no-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\t\ts.Len(user.Emails, 1)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_1234\")\n\t\ts.NotNil(identity)\n\t\ts.Equal(\"test-no-identity@example.com\", identity.Data[\"email\"])\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_linking_succeeded\"}, user.ID.String(), user.Emails.GetPrimary().Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n\nfunc (s *thirdPartySuite) TestThirdPartyHandler_Callback_Link_GoogleToAccountWithGithubIdentity() {\n\tdefer gock.Off()\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/thirdparty\")\n\ts.NoError(err)\n\n\tgock.New(thirdparty.GoogleOauthTokenEndpoint).\n\t\tPost(\"/\").\n\t\tReply(200).\n\t\tJSON(map[string]string{\"access_token\": \"fakeAccessToken\"})\n\n\tgock.New(thirdparty.GoogleUserInfoEndpoint).\n\t\tGet(\"/\").\n\t\tReply(200).\n\t\tJSON(&thirdparty.GoogleUser{\n\t\t\tID:            \"google_1234\",\n\t\t\tEmail:         \"test-with-github-identity@example.com\",\n\t\t\tEmailVerified: true,\n\t\t})\n\n\tcfg := s.setUpConfig([]string{\"google\", \"github\"}, []string{\"https://example.com\"})\n\n\tstate, err := thirdparty.GenerateState(cfg, \"google\", \"https://example.com\")\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/thirdparty/callback?code=abcde&state=%s\", state), nil)\n\treq.AddCookie(&http.Cookie{\n\t\tName:  utils.HankoThirdpartyStateCookie,\n\t\tValue: string(state),\n\t})\n\n\tc, rec := s.setUpContext(req)\n\thandler := s.setUpHandler(cfg)\n\n\tif s.NoError(handler.Callback(c)) {\n\t\ts.Equal(http.StatusTemporaryRedirect, rec.Code)\n\n\t\ts.assertLocationHeaderHasToken(rec)\n\t\ts.assertStateCookieRemoved(rec)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(\"test-with-github-identity@example.com\")\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t\ts.Len(email.Identities, 2)\n\n\t\tuser, err := s.Storage.GetUserPersister().Get(*email.UserID)\n\t\ts.NoError(err)\n\t\ts.NotNil(user)\n\t\ts.Len(user.Emails, 1)\n\n\t\tidentity := email.Identities.GetIdentity(\"google\", \"google_1234\")\n\t\ts.NotNil(identity)\n\t\ts.Equal(\"test-with-github-identity@example.com\", identity.Data[\"email\"])\n\n\t\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"thirdparty_linking_succeeded\"}, user.ID.String(), user.Emails.GetPrimary().Address, \"\", \"\")\n\t\ts.NoError(lerr)\n\t\ts.Len(logs, 1)\n\t}\n}\n"
  },
  {
    "path": "backend/handler/thirdparty_test.go",
    "content": "package handler\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\tjwk2 \"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/stretchr/testify/suite\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk/local_db\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n)\n\nfunc TestThirdPartySuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(thirdPartySuite))\n}\n\ntype thirdPartySuite struct {\n\ttest.Suite\n}\n\nfunc (s *thirdPartySuite) setUpContext(request *http.Request) (echo.Context, *httptest.ResponseRecorder) {\n\ts.T().Helper()\n\te := echo.New()\n\te.Validator = dto.NewCustomValidator()\n\trec := httptest.NewRecorder()\n\tc := e.NewContext(request, rec)\n\treturn c, rec\n}\n\nfunc (s *thirdPartySuite) setUpHandler(cfg *config.Config) *ThirdPartyHandler {\n\ts.T().Helper()\n\tauditLogger := auditlog.NewLogger(s.Storage, cfg.AuditLog)\n\n\tjwkMngr, err := local_db.NewDefaultManager(cfg.Secrets.Keys, s.Storage.GetJwkPersister())\n\ts.Require().NoError(err)\n\n\tsessionMngr, err := session.NewManager(jwkMngr, *cfg)\n\ts.Require().NoError(err)\n\n\thandler := NewThirdPartyHandler(cfg, s.Storage, sessionMngr, auditLogger)\n\treturn handler\n}\n\nfunc (s *thirdPartySuite) setUpConfig(enabledProviders []string, allowedRedirectURLs []string) *config.Config {\n\ts.T().Helper()\n\tcfg := config.DefaultConfig()\n\tcfg.ThirdParty = config.ThirdParty{\n\t\tProviders: config.ThirdPartyProviders{\n\t\t\tApple: config.ThirdPartyProvider{\n\t\t\t\tID:           \"apple\",\n\t\t\t\tEnabled:      false,\n\t\t\t\tClientID:     \"fakeClientID\",\n\t\t\t\tSecret:       \"fakeClientSecret\",\n\t\t\t\tAllowLinking: true,\n\t\t\t},\n\t\t\tGoogle: config.ThirdPartyProvider{\n\t\t\t\tID:           \"google\",\n\t\t\t\tEnabled:      false,\n\t\t\t\tClientID:     \"fakeClientID\",\n\t\t\t\tSecret:       \"fakeClientSecret\",\n\t\t\t\tAllowLinking: true,\n\t\t\t},\n\t\t\tGitHub: config.ThirdPartyProvider{\n\t\t\t\tID:           \"github\",\n\t\t\t\tEnabled:      false,\n\t\t\t\tClientID:     \"fakeClientID\",\n\t\t\t\tSecret:       \"fakeClientSecret\",\n\t\t\t\tAllowLinking: true,\n\t\t\t},\n\t\t\tDiscord: config.ThirdPartyProvider{\n\t\t\t\tID:           \"discord\",\n\t\t\t\tEnabled:      false,\n\t\t\t\tClientID:     \"fakeClientID\",\n\t\t\t\tSecret:       \"fakeClientSecret\",\n\t\t\t\tAllowLinking: true,\n\t\t\t},\n\t\t\tMicrosoft: config.ThirdPartyProvider{\n\t\t\t\tID:           \"microsoft\",\n\t\t\t\tEnabled:      false,\n\t\t\t\tClientID:     \"fakeClientID\",\n\t\t\t\tSecret:       \"fakeClientSecret\",\n\t\t\t\tAllowLinking: false,\n\t\t\t},\n\t\t\tFacebook: config.ThirdPartyProvider{\n\t\t\t\tID:           \"facebook\",\n\t\t\t\tEnabled:      false,\n\t\t\t\tClientID:     \"fakeClientID\",\n\t\t\t\tSecret:       \"fakeClientSecret\",\n\t\t\t\tAllowLinking: false,\n\t\t\t},\n\t\t},\n\t\tErrorRedirectURL:    \"https://error.test.example\",\n\t\tRedirectURL:         \"https://api.test.example/callback\",\n\t\tAllowedRedirectURLS: allowedRedirectURLs,\n\t}\n\n\tcfg.AuditLog.Storage.Enabled = true\n\tcfg.AuditLog.Mask = false\n\tcfg.Email.Limit = 5\n\tcfg.Account.AllowSignup = true\n\n\tfor _, provider := range enabledProviders {\n\t\tswitch provider {\n\t\tcase \"apple\":\n\t\t\tcfg.ThirdParty.Providers.Apple.Enabled = true\n\t\tcase \"google\":\n\t\t\tcfg.ThirdParty.Providers.Google.Enabled = true\n\t\tcase \"github\":\n\t\t\tcfg.ThirdParty.Providers.GitHub.Enabled = true\n\t\tcase \"discord\":\n\t\t\tcfg.ThirdParty.Providers.Discord.Enabled = true\n\t\tcase \"microsoft\":\n\t\t\tcfg.ThirdParty.Providers.Microsoft.Enabled = true\n\t\tcase \"facebook\":\n\t\t\tcfg.ThirdParty.Providers.Facebook.Enabled = true\n\t\t}\n\t}\n\n\terr := cfg.PostProcess()\n\ts.Require().NoError(err)\n\n\treturn cfg\n}\n\nfunc (s *thirdPartySuite) setUpFakeJwkSet() jwk2.Set {\n\ts.T().Helper()\n\tgenerator := test.JwkManager{}\n\tkeySet, err := generator.GetPublicKeys()\n\ts.Require().NoError(err)\n\treturn keySet\n}\n\nfunc (s *thirdPartySuite) setUpAppleIdToken(sub, aud, email string, emailVerified bool, emailVerifiedTypeBool bool) string {\n\ts.T().Helper()\n\ttoken := jwt.New()\n\t_ = token.Set(jwt.SubjectKey, sub)\n\t_ = token.Set(jwt.IssuedAtKey, time.Now().UTC())\n\t_ = token.Set(jwt.IssuerKey, \"https://appleid.apple.com\")\n\t_ = token.Set(jwt.AudienceKey, aud)\n\t_ = token.Set(\"email\", email)\n\tif emailVerifiedTypeBool {\n\t\t_ = token.Set(\"email_verified\", emailVerified)\n\t} else {\n\t\t_ = token.Set(\"email_verified\", strconv.FormatBool(emailVerified))\n\t}\n\n\tgenerator := test.JwkManager{}\n\tsigningKey, err := generator.GetSigningKey()\n\ts.Require().NoError(err)\n\n\tsignedToken, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, signingKey))\n\ts.Require().NoError(err)\n\n\treturn string(signedToken)\n}\n\nfunc (s *thirdPartySuite) setUpMicrosoftIdToken(sub, aud, email string, edov bool) string {\n\ts.T().Helper()\n\ttoken := jwt.New()\n\t_ = token.Set(jwt.SubjectKey, sub)\n\t_ = token.Set(jwt.IssuedAtKey, time.Now().UTC())\n\t_ = token.Set(jwt.IssuerKey, \"https://login.microsoftonline.com/0ec22c9c-397e-484d-8edc-6212147ebe5b/v2.0\")\n\t_ = token.Set(jwt.AudienceKey, aud)\n\t_ = token.Set(\"email\", email)\n\t_ = token.Set(\"xms_edov\", edov)\n\n\tgenerator := test.JwkManager{}\n\tsigningKey, err := generator.GetSigningKey()\n\ts.Require().NoError(err)\n\n\tsignedToken, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, signingKey))\n\ts.Require().NoError(err)\n\n\treturn string(signedToken)\n}\n\nfunc (s *thirdPartySuite) assertLocationHeaderHasToken(rec *httptest.ResponseRecorder) {\n\ts.T().Helper()\n\tlocation, err := url.Parse(rec.Header().Get(\"Location\"))\n\ts.NoError(err)\n\ts.True(location.Query().Has(utils.HankoTokenQuery))\n\ts.NotEmpty(location.Query().Get(utils.HankoTokenQuery))\n}\n\nfunc (s *thirdPartySuite) assertStateCookieRemoved(rec *httptest.ResponseRecorder) {\n\ts.T().Helper()\n\tcookies := rec.Result().Cookies()\n\ts.Len(cookies, 1)\n\ts.Equal(utils.HankoThirdpartyStateCookie, cookies[0].Name)\n\ts.Equal(-1, cookies[0].MaxAge)\n}\n"
  },
  {
    "path": "backend/handler/token.go",
    "content": "package handler\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/sethvargo/go-limiter\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\trateLimit \"github.com/teamhanko/hanko/backend/v2/rate_limiter\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\ntype TokenHandler struct {\n\tpersister      persistence.Persister\n\tsessionManager session.Manager\n\tcfg            *config.Config\n\tauditLogger    auditlog.Logger\n\trateLimiter    limiter.Store\n}\n\nfunc NewTokenHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) *TokenHandler {\n\tvar rateLimiter limiter.Store\n\tif cfg.RateLimiter.Enabled {\n\t\trateLimiter = rateLimit.NewRateLimiter(cfg.RateLimiter, cfg.RateLimiter.TokenLimits)\n\t}\n\n\treturn &TokenHandler{cfg: cfg,\n\t\tpersister:      persister,\n\t\tsessionManager: sessionManager,\n\t\tauditLogger:    auditLogger,\n\t\trateLimiter:    rateLimiter,\n\t}\n}\n\ntype TokenValidationBody struct {\n\tValue string `json:\"value\" validate:\"required\"`\n}\n\nfunc (h TokenHandler) Validate(c echo.Context) error {\n\tif h.rateLimiter != nil {\n\t\terr := rateLimit.Limit(h.rateLimiter, uuid.Nil, c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar userID uuid.UUID\n\terr := h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tvar body TokenValidationBody\n\t\tif terr := (&echo.DefaultBinder{}).BindBody(c, &body); terr != nil {\n\t\t\treturn dto.ToHttpError(terr)\n\t\t}\n\n\t\tif terr := c.Validate(body); terr != nil {\n\t\t\treturn dto.ToHttpError(terr)\n\t\t}\n\n\t\ttokenPersister := h.persister.GetTokenPersisterWithConnection(tx)\n\t\ttoken, terr := tokenPersister.GetByValue(body.Value)\n\t\tif terr != nil {\n\t\t\treturn fmt.Errorf(\"failed to fetch token from db: %w\", terr)\n\t\t}\n\n\t\tif token == nil {\n\t\t\treturn echo.NewHTTPError(http.StatusNotFound, \"token not found\")\n\t\t}\n\n\t\tif time.Now().UTC().After(token.ExpiresAt) {\n\t\t\treturn echo.NewHTTPError(http.StatusUnprocessableEntity, \"token has expired\")\n\t\t}\n\n\t\tterr = tokenPersister.Delete(*token)\n\t\tif terr != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete token from db: %w\", terr)\n\t\t}\n\n\t\temails, err := h.persister.GetEmailPersister().FindByUserId(token.UserID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get emails from db: %w\", err)\n\t\t}\n\n\t\tvar emailJwt *dto.EmailJWT\n\t\tif e := emails.GetPrimary(); e != nil {\n\t\t\temailJwt = dto.EmailJWTFromEmailModel(e)\n\t\t}\n\n\t\tjwtToken, rawToken, err := h.sessionManager.GenerateJWT(dto.UserJWT{\n\t\t\tUserID: token.UserID.String(),\n\t\t\tEmail:  emailJwt,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to generate jwt: %w\", err)\n\t\t}\n\n\t\tcookie, err := h.sessionManager.GenerateCookie(jwtToken)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create session token: %w\", err)\n\t\t}\n\n\t\terr = storeSession(h.cfg, h.persister, token.UserID, rawToken, c, h.persister.GetConnection())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store session in DB: %w\", err)\n\t\t}\n\n\t\tc.Response().Header().Set(\"X-Session-Lifetime\", fmt.Sprintf(\"%d\", cookie.MaxAge))\n\n\t\tif h.cfg.Session.EnableAuthTokenHeader {\n\t\t\tc.Response().Header().Set(\"X-Auth-Token\", jwtToken)\n\t\t} else {\n\t\t\tc.SetCookie(cookie)\n\t\t}\n\n\t\tuserID = token.UserID\n\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tvar httpError *echo.HTTPError\n\t\tif errors.As(err, &httpError) {\n\t\t\taerr := h.auditLogger.Create(c, models.AuditLogTokenExchangeFailed, nil, err)\n\t\t\tif aerr != nil {\n\t\t\t\treturn fmt.Errorf(\"could not create audit log: %w\", aerr)\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n\n\tuser := &models.User{ID: userID}\n\terr = h.auditLogger.Create(c, models.AuditLogTokenExchangeSucceeded, user, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create audit log: %w\", err)\n\t}\n\n\treturn c.JSON(http.StatusOK, map[string]string{\"user_id\": userID.String()})\n\n}\n"
  },
  {
    "path": "backend/handler/token_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestTokenSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(tokenSuite))\n}\n\ntype tokenSuite struct {\n\ttest.Suite\n}\n\nfunc (s *tokenSuite) TestToken_Validate_TokenInCookie() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/token\")\n\n\t// must create and insert a valid token manually instead of using fixtures, because token\n\t// validation is time sensitive (expiration is checked relative to current time)\n\tuId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\ttoken, err := models.NewToken(uId)\n\ts.NoError(err)\n\terr = s.Storage.GetTokenPersister().Create(*token)\n\ts.NoError(err)\n\n\tbody := TokenValidationBody{Value: token.Value}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/token\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\tcfg := s.setupConfig()\n\tcfg.Session.EnableAuthTokenHeader = false\n\te := NewPublicRouter(cfg, s.Storage, nil, nil)\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(rec.Code, http.StatusOK)\n\tt, err := s.Storage.GetTokenPersister().GetByValue(token.Value)\n\ts.NoError(err)\n\ts.Nil(t)\n\n\ts.Empty(rec.Header().Get(\"X-Auth-Token\"))\n\tcookies := rec.Result().Cookies()\n\trec.Result().Cookies()\n\ts.NotEmpty(cookies)\n\tfor _, cookie := range cookies {\n\t\tif cookie.Name == \"hanko\" {\n\t\t\ts.Regexp(\".*\\\\..*\\\\..*\", cookie.Value)\n\t\t}\n\t}\n\n\tlogs, err := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"token_exchange_succeeded\"}, \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\", \"\", \"\", \"\")\n\ts.Len(logs, 1)\n}\n\nfunc (s *tokenSuite) TestToken_Validate_TokenInHeader() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/token\")\n\n\t// must create and insert a valid token manually instead of using fixtures, because token\n\t// validation is time sensitive (expiration is checked relative to current time)\n\tuId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\ttoken, err := models.NewToken(uId)\n\ts.NoError(err)\n\terr = s.Storage.GetTokenPersister().Create(*token)\n\ts.NoError(err)\n\n\tbody := TokenValidationBody{Value: token.Value}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/token\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\tcfg := s.setupConfig()\n\te := NewPublicRouter(cfg, s.Storage, nil, nil)\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(rec.Code, http.StatusOK)\n\tt, err := s.Storage.GetTokenPersister().GetByValue(token.Value)\n\ts.NoError(err)\n\ts.Nil(t)\n\n\ts.Empty(rec.Result().Cookies())\n\tresponseToken := rec.Header().Get(\"X-Auth-Token\")\n\ts.NotEmpty(responseToken)\n\ts.Regexp(\".*\\\\..*\\\\..*\", responseToken)\n\n\tlogs, err := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"token_exchange_succeeded\"}, \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\", \"\", \"\", \"\")\n\ts.Len(logs, 1)\n}\n\nfunc (s *tokenSuite) TestToken_Validate_ExpiredToken() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/token\")\n\n\texpiredTokenValue := \"Trkauhl3q7XVxw5JcDH80lTe1KxzydIw0OcizH7umWk=\"\n\tbody := TokenValidationBody{Value: expiredTokenValue}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/token\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te := NewPublicRouter(s.setupConfig(), s.Storage, nil, nil)\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(rec.Code, http.StatusUnprocessableEntity)\n\tvar errorResponse echo.HTTPError\n\tmarshalErr := json.Unmarshal(rec.Body.Bytes(), &errorResponse)\n\ts.NoError(marshalErr)\n\ts.Contains(errorResponse.Message, \"expired\")\n\n\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"token_exchange_failed\"}, \"\", \"\", \"\", \"\")\n\ts.NoError(lerr)\n\ts.Len(logs, 1)\n}\n\nfunc (s *tokenSuite) TestToken_Validate_MissingTokenFromRequest() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\treq := httptest.NewRequest(http.MethodPost, \"/token\", nil)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te := NewPublicRouter(s.setupConfig(), s.Storage, nil, nil)\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(rec.Code, http.StatusBadRequest)\n\tvar errorResponse echo.HTTPError\n\tmarshalErr := json.Unmarshal(rec.Body.Bytes(), &errorResponse)\n\ts.NoError(marshalErr)\n\ts.Contains(\"value is a required field\", errorResponse.Message)\n\n\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"token_exchange_failed\"}, \"\", \"\", \"\", \"\")\n\ts.NoError(lerr)\n\ts.Len(logs, 1)\n}\n\nfunc (s *tokenSuite) TestToken_Validate_InvalidJson() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\treq := httptest.NewRequest(http.MethodPost, \"/token\", bytes.NewReader([]byte(\"invalid\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te := NewPublicRouter(s.setupConfig(), s.Storage, nil, nil)\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(rec.Code, http.StatusBadRequest)\n\n\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"token_exchange_failed\"}, \"\", \"\", \"\", \"\")\n\ts.NoError(lerr)\n\ts.Len(logs, 1)\n\n}\n\nfunc (s *tokenSuite) TestToken_Validate_TokenNotFound() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tuId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\ttoken, err := models.NewToken(uId)\n\ts.NoError(err)\n\n\tbody := TokenValidationBody{Value: token.Value}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/token\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te := NewPublicRouter(s.setupConfig(), s.Storage, nil, nil)\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(rec.Code, http.StatusNotFound)\n\tvar errorResponse echo.HTTPError\n\tmarshalErr := json.Unmarshal(rec.Body.Bytes(), &errorResponse)\n\ts.NoError(marshalErr)\n\ts.Contains(\"token not found\", errorResponse.Message)\n\n\tlogs, lerr := s.Storage.GetAuditLogPersister().List(0, 0, nil, nil, []string{\"token_exchange_failed\"}, \"\", \"\", \"\", \"\")\n\ts.NoError(lerr)\n\ts.Len(logs, 1)\n}\n\nfunc (s *tokenSuite) setupConfig() *config.Config {\n\tcfg := test.DefaultConfig\n\tcfg.Session.EnableAuthTokenHeader = true\n\tcfg.AuditLog.Storage.Enabled = true\n\treturn &cfg\n}\n"
  },
  {
    "path": "backend/handler/user.go",
    "content": "package handler\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype UserHandler struct {\n\tpersister      persistence.Persister\n\tsessionManager session.Manager\n\tauditLogger    auditlog.Logger\n\tcfg            *config.Config\n}\n\nfunc NewUserHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) *UserHandler {\n\treturn &UserHandler{\n\t\tpersister:      persister,\n\t\tauditLogger:    auditLogger,\n\t\tsessionManager: sessionManager,\n\t\tcfg:            cfg,\n\t}\n}\n\ntype UserCreateBody struct {\n\tEmail string `json:\"email\" validate:\"required,email\"`\n}\n\nfunc (h *UserHandler) Create(c echo.Context) error {\n\tif !h.cfg.Account.AllowSignup {\n\t\treturn echo.NewHTTPError(http.StatusForbidden).SetInternal(errors.New(\"account signup is disabled\"))\n\t}\n\n\tvar body UserCreateBody\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := c.Validate(body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tbody.Email = strings.ToLower(body.Email)\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tnewUser := models.NewUser()\n\t\terr := h.persister.GetUserPersisterWithConnection(tx).Create(newUser)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store user: %w\", err)\n\t\t}\n\n\t\temail, err := h.persister.GetEmailPersisterWithConnection(tx).FindByAddress(body.Email)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get email: %w\", err)\n\t\t}\n\n\t\tif email != nil {\n\t\t\tif email.UserID != nil {\n\t\t\t\t// The email already exists and is assigned already.\n\t\t\t\treturn echo.NewHTTPError(http.StatusConflict).SetInternal(errors.New(fmt.Sprintf(\"user with email %s already exists\", body.Email)))\n\t\t\t}\n\n\t\t\tif !h.cfg.Email.RequireVerification {\n\t\t\t\t// Assign the email address to the user because it's currently unassigned and email verification is turned off.\n\t\t\t\temail.UserID = &newUser.ID\n\n\t\t\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Update(*email)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to update email address: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// The email address does not exist, create a new one.\n\t\t\tif h.cfg.Email.RequireVerification {\n\t\t\t\t// The email can only be assigned to the user via passcode verification.\n\t\t\t\temail = models.NewEmail(nil, body.Email)\n\t\t\t} else {\n\t\t\t\temail = models.NewEmail(&newUser.ID, body.Email)\n\t\t\t}\n\n\t\t\terr = h.persister.GetEmailPersisterWithConnection(tx).Create(*email)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to store user: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tif !h.cfg.Email.RequireVerification {\n\t\t\tprimaryEmail := models.NewPrimaryEmail(email.ID, newUser.ID)\n\t\t\terr = h.persister.GetPrimaryEmailPersisterWithConnection(tx).Create(*primaryEmail)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to store primary email: %w\", err)\n\t\t\t}\n\n\t\t\temails, err := h.persister.GetEmailPersisterWithConnection(tx).FindByUserId(newUser.ID)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to get email from db: %w\", err)\n\t\t\t}\n\n\t\t\tvar emailJwt *dto.EmailJWT\n\t\t\tif e := emails.GetPrimary(); e != nil {\n\t\t\t\temailJwt = dto.EmailJWTFromEmailModel(e)\n\t\t\t}\n\n\t\t\ttoken, _, err := h.sessionManager.GenerateJWT(dto.UserJWT{\n\t\t\t\tUserID: newUser.ID.String(),\n\t\t\t\tEmail:  emailJwt,\n\t\t\t})\n\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to generate jwt: %w\", err)\n\t\t\t}\n\n\t\t\tcookie, err := h.sessionManager.GenerateCookie(token)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create session token: %w\", err)\n\t\t\t}\n\n\t\t\tc.Response().Header().Set(\"X-Session-Lifetime\", fmt.Sprintf(\"%d\", cookie.MaxAge))\n\n\t\t\tif h.cfg.Session.EnableAuthTokenHeader {\n\t\t\t\tc.Response().Header().Set(\"X-Auth-Token\", token)\n\t\t\t} else {\n\t\t\t\tc.SetCookie(cookie)\n\t\t\t}\n\t\t}\n\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogUserCreated, &newUser, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write audit log: %w\", err)\n\t\t}\n\n\t\t// This cookie is a workaround for hanko element versions before 0.1.0-alpha,\n\t\t// because else the backend would not know where to send the first passcode.\n\t\tc.SetCookie(&http.Cookie{\n\t\t\tName:     \"hanko_email_id\",\n\t\t\tValue:    email.ID.String(),\n\t\t\tDomain:   h.cfg.Session.Cookie.Domain,\n\t\t\tSecure:   h.cfg.Session.Cookie.Secure,\n\t\t\tHttpOnly: h.cfg.Session.Cookie.HttpOnly,\n\t\t\tSameSite: http.SameSiteNoneMode,\n\t\t})\n\n\t\tnewUserDto := dto.CreateUserResponse{\n\t\t\tID:      newUser.ID,\n\t\t\tUserID:  newUser.ID,\n\t\t\tEmailID: email.ID,\n\t\t}\n\n\t\tif !h.cfg.Email.RequireVerification {\n\t\t\terr = utils.TriggerWebhooks(c, tx, events.UserCreate, admin.FromUserModel(newUser))\n\t\t\tif err != nil {\n\t\t\t\tc.Logger().Warn(err)\n\t\t\t}\n\t\t}\n\n\t\treturn c.JSON(http.StatusOK, newUserDto)\n\t})\n}\n\nfunc (h *UserHandler) Get(c echo.Context) error {\n\tuserId := c.Param(\"id\")\n\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"missing or malformed jwt\")\n\t}\n\n\tif sessionToken.Subject() != userId {\n\t\treturn echo.NewHTTPError(http.StatusForbidden).SetInternal(errors.New(fmt.Sprintf(\"user %s tried to get user %s\", sessionToken.Subject(), userId)))\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(uuid.FromStringOrNil(userId))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"user not found\"))\n\t}\n\n\tvar emailAddress *string\n\tif e := user.Emails.GetPrimary(); e != nil {\n\t\temailAddress = &e.Address\n\t}\n\n\tvar metadata *dto.Metadata\n\tif user.Metadata != nil {\n\t\tmetadata = dto.NewMetadata(user.Metadata)\n\t}\n\n\treturn c.JSON(http.StatusOK, dto.GetUserResponse{\n\t\tID:                  user.ID,\n\t\tWebauthnCredentials: user.WebauthnCredentials,\n\t\tEmail:               emailAddress,\n\t\tUsername:            user.GetUsername(),\n\t\tCreatedAt:           user.CreatedAt,\n\t\tUpdatedAt:           user.UpdatedAt,\n\t\tMetadata:            metadata,\n\t\tProfileData:         *dto.ProfileDataFromUserModel(user, h.cfg),\n\t})\n}\n\ntype UserGetByEmailBody struct {\n\tEmail string `json:\"email\" validate:\"required,email\"`\n}\n\nfunc (h *UserHandler) GetUserIdByEmail(c echo.Context) error {\n\tvar request UserGetByEmailBody\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &request); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := c.Validate(request); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\temailAddress := strings.ToLower(request.Email)\n\temail, err := h.persister.GetEmailPersister().FindByAddress(emailAddress)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tif email == nil || email.UserID == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"user not found\"))\n\t}\n\n\tcredentials, err := h.persister.GetWebauthnCredentialPersister().GetFromUser(*email.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get webauthn credentials: %w\", err)\n\t}\n\n\treturn c.JSON(http.StatusOK, dto.UserInfoResponse{\n\t\tID:                    *email.UserID,\n\t\tVerified:              email.Verified,\n\t\tEmailID:               email.ID,\n\t\tHasWebauthnCredential: len(credentials) > 0,\n\t})\n}\n\nfunc (h *UserHandler) Me(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"failed to cast session object\")\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(uuid.FromStringOrNil(sessionToken.Subject()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"user not found\"))\n\t}\n\n\tdata := dto.ProfileDataFromUserModel(user, h.cfg)\n\treturn c.JSON(http.StatusOK, *data)\n}\n\nfunc (h *UserHandler) Delete(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"missing or malformed jwt\")\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse subject as uuid: %w\", err)\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tuser, err := h.persister.GetUserPersisterWithConnection(tx).Get(userId)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t\t}\n\n\t\tif user == nil {\n\t\t\treturn fmt.Errorf(\"unknown user\")\n\t\t}\n\n\t\terr = h.persister.GetUserPersisterWithConnection(tx).Delete(*user)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete user: %w\", err)\n\t\t}\n\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogUserDeleted, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write audit log: %w\", err)\n\t\t}\n\n\t\tcookie, err := h.sessionManager.DeleteCookie()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create session token: %w\", err)\n\t\t}\n\n\t\tc.SetCookie(cookie)\n\n\t\terr = utils.TriggerWebhooks(c, tx, events.UserDelete, admin.FromUserModel(*user))\n\t\tif err != nil {\n\t\t\tc.Logger().Warn(err)\n\t\t}\n\n\t\treturn c.NoContent(http.StatusNoContent)\n\t})\n}\n\nfunc (h *UserHandler) Logout(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(\"missing or malformed jwt\")\n\t}\n\n\tuserId := uuid.FromStringOrNil(sessionToken.Subject())\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tsID, ok := sessionToken.Get(\"session_id\")\n\tif ok {\n\t\tsessionIDString := sID.(string)\n\t\tsessionID, err := uuid.FromString(sessionIDString)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to convert session id to uuid: %w\", err)\n\t\t}\n\t\tsessionModel, err := h.persister.GetSessionPersister().Get(sessionID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get session from database: %w\", err)\n\t\t}\n\t\tif sessionModel != nil {\n\t\t\terr = h.persister.GetSessionPersister().Delete(*sessionModel)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to delete session from database: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\terr = h.auditLogger.Create(c, models.AuditLogUserLoggedOut, user, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to write audit log: %w\", err)\n\t}\n\n\tcookie, err := h.sessionManager.DeleteCookie()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create session token: %w\", err)\n\t}\n\n\tc.SetCookie(cookie)\n\n\treturn c.NoContent(http.StatusNoContent)\n}\n"
  },
  {
    "path": "backend/handler/user_admin.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-sql-driver/mysql\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/jackc/pgconn\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/pagination\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\twebhookUtils \"github.com/teamhanko/hanko/backend/v2/webhooks/utils\"\n)\n\ntype UserHandlerAdmin struct {\n\tpersister persistence.Persister\n}\n\nfunc NewUserHandlerAdmin(persister persistence.Persister) *UserHandlerAdmin {\n\treturn &UserHandlerAdmin{persister: persister}\n}\n\nfunc (h *UserHandlerAdmin) Delete(c echo.Context) error {\n\tuserId, err := uuid.FromString(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse userId as uuid\").SetInternal(err)\n\t}\n\n\terr = h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tp := h.persister.GetUserPersisterWithConnection(tx)\n\t\tuser, err := p.Get(userId)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t\t}\n\n\t\tif user == nil {\n\t\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\")\n\t\t}\n\n\t\terr = p.Delete(*user)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete user: %w\", err)\n\t\t}\n\n\t\terr = webhookUtils.TriggerWebhooks(c, tx, events.UserDelete, admin.FromUserModel(*user))\n\t\tif err != nil {\n\t\t\tc.Logger().Warn(err)\n\t\t}\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn c.NoContent(http.StatusNoContent)\n}\n\ntype UserListRequest struct {\n\tPerPage       int    `query:\"per_page\"`\n\tPage          int    `query:\"page\"`\n\tEmail         string `query:\"email\"`\n\tUserID        string `query:\"user_id\"`\n\tUsername      string `query:\"username\"`\n\tSortDirection string `query:\"sort_direction\"`\n}\n\nfunc (h *UserHandlerAdmin) List(c echo.Context) error {\n\tvar request UserListRequest\n\terr := (&echo.DefaultBinder{}).BindQueryParams(c, &request)\n\tif err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif request.Page == 0 {\n\t\trequest.Page = 1\n\t}\n\n\tif request.PerPage == 0 {\n\t\trequest.PerPage = 20\n\t}\n\n\tvar userIDs []uuid.UUID\n\tif request.UserID != \"\" {\n\t\tfor _, userIDString := range strings.Split(request.UserID, \",\") {\n\t\t\tuserID, err := uuid.FromString(userIDString)\n\t\t\tif err != nil {\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse user_id as uuid\").SetInternal(err)\n\t\t\t}\n\t\t\tuserIDs = append(userIDs, userID)\n\t\t}\n\t}\n\n\tif request.SortDirection == \"\" {\n\t\trequest.SortDirection = \"desc\"\n\t}\n\n\tswitch request.SortDirection {\n\tcase \"desc\", \"asc\":\n\tdefault:\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"sort_direction must be desc or asc\")\n\t}\n\n\temail := strings.ToLower(request.Email)\n\tusername := strings.ToLower(request.Username)\n\n\tusers, err := h.persister.GetUserPersister().List(request.Page, request.PerPage, userIDs, email, username, request.SortDirection)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get list of users: %w\", err)\n\t}\n\n\tuserCount, err := h.persister.GetUserPersister().Count(userIDs, email, username)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get total count of users: %w\", err)\n\t}\n\n\tu, _ := url.Parse(fmt.Sprintf(\"%s://%s%s\", c.Scheme(), c.Request().Host, c.Request().RequestURI))\n\n\tc.Response().Header().Set(\"Link\", pagination.CreateHeader(u, userCount, request.Page, request.PerPage))\n\tc.Response().Header().Set(\"X-Total-Count\", strconv.FormatInt(int64(userCount), 10))\n\n\tl := make([]admin.User, len(users))\n\tfor i := range users {\n\t\tl[i] = admin.FromUserModel(users[i])\n\t}\n\n\treturn c.JSON(http.StatusOK, l)\n}\n\nfunc (h *UserHandlerAdmin) Get(c echo.Context) error {\n\tuserId, err := uuid.FromString(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse userId as uuid\").SetInternal(err)\n\t}\n\n\tp := h.persister.GetUserPersister()\n\tuser, err := p.Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\")\n\t}\n\n\treturn c.JSON(http.StatusOK, admin.FromUserModel(*user))\n}\n\nfunc (h *UserHandlerAdmin) Create(c echo.Context) error {\n\tvar body admin.CreateUser\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif err := c.Validate(body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tif len(body.Emails) == 0 && (body.Username == nil || *body.Username == \"\") {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"at least one of [Emails, Username] must be set\")\n\t}\n\n\t// if no userID is provided, create a new one\n\tif body.ID.IsNil() {\n\t\tuserId, err := uuid.NewV4()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create new userId: %w\", err)\n\t\t}\n\t\tbody.ID = userId\n\t}\n\n\t// check that only one email is marked as primary\n\tprimaryEmails := 0\n\tfor _, email := range body.Emails {\n\t\tif email.IsPrimary {\n\t\t\tprimaryEmails++\n\t\t}\n\t}\n\n\tif primaryEmails == 0 && len(body.Emails) > 0 {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"at least one primary email must be provided\")\n\t} else if primaryEmails > 1 {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"only one primary email is allowed\")\n\t}\n\n\terr := h.persister.GetConnection().Transaction(func(tx *pop.Connection) error {\n\t\tu := models.User{\n\t\t\tID:        body.ID,\n\t\t\tCreatedAt: body.CreatedAt,\n\t\t}\n\n\t\terr := tx.Create(&u)\n\t\tif err != nil {\n\t\t\tvar pgErr *pgconn.PgError\n\t\t\tvar mysqlErr *mysql.MySQLError\n\t\t\tif errors.As(err, &pgErr) {\n\t\t\t\tif pgErr.Code == \"23505\" {\n\t\t\t\t\treturn echo.NewHTTPError(http.StatusConflict, fmt.Errorf(\"failed to create user with id '%v': %w\", u.ID, fmt.Errorf(\"user already exists\")))\n\t\t\t\t}\n\t\t\t} else if errors.As(err, &mysqlErr) {\n\t\t\t\tif mysqlErr.Number == 1062 {\n\t\t\t\t\treturn echo.NewHTTPError(http.StatusConflict, fmt.Errorf(\"failed to create user with id '%v': %w\", u.ID, fmt.Errorf(\"user already exists\")))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"failed to create user with id '%v': %w\", u.ID, err)\n\t\t}\n\n\t\tnow := time.Now()\n\t\tfor _, email := range body.Emails {\n\t\t\temailId, _ := uuid.NewV4()\n\t\t\tmail := models.Email{\n\t\t\t\tID:        emailId,\n\t\t\t\tUserID:    &u.ID,\n\t\t\t\tAddress:   strings.ToLower(email.Address),\n\t\t\t\tVerified:  email.IsVerified,\n\t\t\t\tCreatedAt: now,\n\t\t\t\tUpdatedAt: now,\n\t\t\t}\n\n\t\t\terr := tx.Create(&mail)\n\t\t\tif err != nil {\n\t\t\t\tvar pgErr *pgconn.PgError\n\t\t\t\tvar mysqlErr *mysql.MySQLError\n\t\t\t\tif errors.As(err, &pgErr) {\n\t\t\t\t\tif pgErr.Code == \"23505\" {\n\t\t\t\t\t\treturn echo.NewHTTPError(http.StatusConflict, fmt.Errorf(\"failed to create email '%s' for user '%v': %w\", mail.Address, u.ID, fmt.Errorf(\"email already exists\")))\n\t\t\t\t\t}\n\t\t\t\t} else if errors.As(err, &mysqlErr) {\n\t\t\t\t\tif mysqlErr.Number == 1062 {\n\t\t\t\t\t\treturn echo.NewHTTPError(http.StatusConflict, fmt.Errorf(\"failed to create email '%s' for user '%v': %w\", mail.Address, u.ID, fmt.Errorf(\"email already exists\")))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"failed to create email '%s' for user '%v': %w\", mail.Address, u.ID, err)\n\t\t\t}\n\n\t\t\tif email.IsPrimary {\n\t\t\t\tprimary := models.PrimaryEmail{\n\t\t\t\t\tUserID:  u.ID,\n\t\t\t\t\tEmailID: mail.ID,\n\t\t\t\t}\n\t\t\t\terr := tx.Create(&primary)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to set email '%s' as primary for user '%v': %w\", mail.Address, u.ID, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif body.Username != nil {\n\t\t\tusername := models.NewUsername(u.ID, *body.Username)\n\t\t\terr = tx.Create(username)\n\t\t\tif err != nil {\n\t\t\t\tvar pgErr *pgconn.PgError\n\t\t\t\tvar mysqlErr *mysql.MySQLError\n\t\t\t\tif errors.As(err, &pgErr) {\n\t\t\t\t\tif pgErr.Code == \"23505\" {\n\t\t\t\t\t\treturn echo.NewHTTPError(http.StatusConflict, fmt.Errorf(\"failed to create username '%s' for user '%v': %w\", username.Username, u.ID, fmt.Errorf(\"username already exists\")))\n\t\t\t\t\t}\n\t\t\t\t} else if errors.As(err, &mysqlErr) {\n\t\t\t\t\tif mysqlErr.Number == 1062 {\n\t\t\t\t\t\treturn echo.NewHTTPError(http.StatusConflict, fmt.Errorf(\"failed to create username '%s' for user '%v': %w\", username.Username, u.ID, fmt.Errorf(\"username already exists\")))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"failed to create email '%s' for user '%v': %w\", username.Username, u.ID, err)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\n\tif httpError, ok := err.(*echo.HTTPError); ok {\n\t\treturn httpError\n\t} else if err != nil {\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, err)\n\t}\n\n\tp := h.persister.GetUserPersister()\n\tuser, err := p.Get(body.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\")\n\t}\n\n\tuserDto := admin.FromUserModel(*user)\n\n\terr = webhookUtils.TriggerWebhooks(c, h.persister.GetConnection(), events.UserCreate, userDto)\n\tif err != nil {\n\t\tc.Logger().Warn(err)\n\t}\n\n\treturn c.JSON(http.StatusOK, userDto)\n}\n\n// OptionalString represents a PATCH-able string field with 3 states:\n// - not present in JSON => Present=false (no change)\n// - present with string => Present=true, Value!=nil (set)\n// - present with null   => Present=true, Value==nil (clear)\ntype OptionalString struct {\n\tPresent bool\n\tValue   *string\n}\n\nfunc (o *OptionalString) UnmarshalJSON(b []byte) error {\n\to.Present = true\n\n\tif bytes.Equal(bytes.TrimSpace(b), []byte(\"null\")) {\n\t\to.Value = nil\n\t\treturn nil\n\t}\n\n\tvar s string\n\tif err := json.Unmarshal(b, &s); err != nil {\n\t\treturn fmt.Errorf(\"expected string or null: %w\", err)\n\t}\n\n\to.Value = &s\n\treturn nil\n}\n\ntype PatchUserAdminRequest struct {\n\tUsername   OptionalString `json:\"username\"`\n\tName       OptionalString `json:\"name\"`\n\tGivenName  OptionalString `json:\"given_name\"`\n\tFamilyName OptionalString `json:\"family_name\"`\n\tPicture    OptionalString `json:\"picture\"`\n}\n\nfunc (h *UserHandlerAdmin) Patch(c echo.Context) error {\n\tuserId, err := uuid.FromString(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse userId as uuid\").SetInternal(err)\n\t}\n\n\tvar body PatchUserAdminRequest\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &body); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\t// Empty/whitespace-only strings are invalid (`null` is used to clear).\n\tnormalizeOptionalString := func(field string, v OptionalString, lower bool) (OptionalString, error) {\n\t\tif !v.Present || v.Value == nil {\n\t\t\treturn v, nil\n\t\t}\n\n\t\ts := strings.TrimSpace(*v.Value)\n\t\tif s == \"\" {\n\t\t\treturn v, echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf(\"%s must be a non-empty string or null\", field))\n\t\t}\n\t\tif lower {\n\t\t\ts = strings.ToLower(s)\n\t\t}\n\t\tv.Value = &s\n\t\treturn v, nil\n\t}\n\n\tbody.Username, err = normalizeOptionalString(\"username\", body.Username, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody.Name, err = normalizeOptionalString(\"name\", body.Name, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody.GivenName, err = normalizeOptionalString(\"given_name\", body.GivenName, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody.FamilyName, err = normalizeOptionalString(\"family_name\", body.FamilyName, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody.Picture, err = normalizeOptionalString(\"picture\", body.Picture, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif body.Picture.Present && body.Picture.Value != nil {\n\t\tif err := utils.ValidatePictureURL(*body.Picture.Value); err != nil {\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"picture must be a valid http(s) URL or null\").SetInternal(err)\n\t\t}\n\t}\n\n\terr = h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tuserPersister := h.persister.GetUserPersisterWithConnection(tx)\n\t\tusernamePersister := h.persister.GetUsernamePersisterWithConnection(tx)\n\n\t\tuser, err := userPersister.Get(userId)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t\t}\n\t\tif user == nil {\n\t\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\")\n\t\t}\n\n\t\tchanged := false\n\n\t\tapplyNullsString := func(dst *nulls.String, in OptionalString) {\n\t\t\tif !in.Present {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif in.Value == nil {\n\t\t\t\t*dst = nulls.String{} // NULL\n\t\t\t} else {\n\t\t\t\t*dst = nulls.NewString(*in.Value)\n\t\t\t}\n\t\t\tchanged = true\n\t\t}\n\n\t\tapplyNullsString(&user.Name, body.Name)\n\t\tapplyNullsString(&user.GivenName, body.GivenName)\n\t\tapplyNullsString(&user.FamilyName, body.FamilyName)\n\t\tapplyNullsString(&user.Picture, body.Picture)\n\n\t\t// Username is currently still stored in its own table/record.\n\t\tif body.Username.Present {\n\t\t\tif body.Username.Value == nil {\n\t\t\t\tif user.Username != nil {\n\t\t\t\t\tif err := usernamePersister.Delete(user.Username); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to delete username: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tuser.DeleteUsername()\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewUsername := *body.Username.Value\n\n\t\t\t\tvalidNewUsername := regexp.MustCompile(`^\\w+$`).MatchString(newUsername)\n\n\t\t\t\tif !validNewUsername {\n\t\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"username is invalid\")\n\t\t\t\t}\n\n\t\t\t\tdup, err := usernamePersister.GetByName(newUsername)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to check duplicate username: %w\", err)\n\t\t\t\t}\n\t\t\t\tif dup != nil && dup.UserId != user.ID {\n\t\t\t\t\treturn echo.NewHTTPError(http.StatusConflict, \"username already exists\")\n\t\t\t\t}\n\n\t\t\t\tif user.Username == nil {\n\t\t\t\t\tusernameModel := models.NewUsername(user.ID, newUsername)\n\t\t\t\t\tif err := usernamePersister.Create(*usernameModel); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to create username: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tuser.SetUsername(usernameModel)\n\t\t\t\t\tchanged = true\n\t\t\t\t} else if user.Username.Username != newUsername {\n\t\t\t\t\tuser.Username.Username = newUsername\n\t\t\t\t\tuser.Username.UpdatedAt = time.Now()\n\t\t\t\t\tif err := usernamePersister.Update(user.Username); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to update username: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif !changed {\n\t\t\treturn nil\n\t\t}\n\n\t\tuser.UpdatedAt = time.Now()\n\t\tif err := userPersister.Update(*user); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to update user: %w\", err)\n\t\t}\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"user not found\")\n\t}\n\n\treturn c.JSON(http.StatusOK, admin.FromUserModel(*user))\n}\n"
  },
  {
    "path": "backend/handler/user_admin_test.go",
    "content": "package handler\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestUserHandlerAdminSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(userAdminSuite))\n}\n\ntype userAdminSuite struct {\n\ttest.Suite\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s\", \"38bf5a00-d7ea-40a5-a5de-48722c148925\"), nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusNoContent, rec.Code)\n\n\tcount, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, \"\", \"\")\n\ts.Require().NoError(err)\n\ts.Equal(3, count)\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_Delete_UnknownUserId() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s\", \"1e5dcc5c-8570-43cb-ba8b-caa88bbfc7ac\"), nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusNotFound, rec.Code)\n\n\tcount, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, \"\", \"\")\n\ts.Require().NoError(err)\n\ts.Equal(4, count)\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_Delete_InvalidUserId() {\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s\", \"invalidId\"), nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_List() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/users\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusOK, rec.Code)\n\ts.Equal(\"4\", rec.Header().Get(\"X-Total-Count\"))\n\ts.Equal(\"<http://example.com/users?page=1&per_page=20>; rel=\\\"first\\\"\", rec.Header().Get(\"Link\"))\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_List_Pagination() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/users?page=1&per_page=1\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Equal(\"4\", rec.Header().Get(\"X-Total-Count\"))\n\n\t\tvar got []models.User\n\t\terr = json.Unmarshal(rec.Body.Bytes(), &got)\n\t\ts.Require().NoError(err)\n\n\t\ts.Equal(1, len(got))\n\t\ts.Equal(\"<http://example.com/users?page=4&per_page=1>; rel=\\\"last\\\",<http://example.com/users?page=2&per_page=1>; rel=\\\"next\\\"\", rec.Header().Get(\"Link\"))\n\t}\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_List_NoUsers() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/users\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Equal(\"0\", rec.Header().Get(\"X-Total-Count\"))\n\n\t\tvar got []models.User\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &got)\n\t\ts.Require().NoError(err)\n\n\t\ts.Equal(0, len(got))\n\t\ts.Equal(\"<http://example.com/users?page=1&per_page=20>; rel=\\\"first\\\"\", rec.Header().Get(\"Link\"))\n\t}\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_List_MultipleUserIDs() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/users?user_id=b5dd5267-b462-48be-b70d-bcd6f1bbe7a5,e0282f3f-b211-4f0e-b777-6fabc69287c9\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Equal(\"2\", rec.Header().Get(\"X-Total-Count\"))\n\n\t\tvar got []models.User\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &got)\n\t\ts.Require().NoError(err)\n\n\t\ts.Equal(2, len(got))\n\t\ts.Equal(\"<http://example.com/users?page=1&per_page=20&user_id=b5dd5267-b462-48be-b70d-bcd6f1bbe7a5%2Ce0282f3f-b211-4f0e-b777-6fabc69287c9>; rel=\\\"first\\\"\", rec.Header().Get(\"Link\"))\n\t}\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_List_InvalidPaginationParam() {\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/users?per_page=invalid\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_Create() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname               string\n\t\tbody               string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"success\",\n\t\t\tbody:               `{\"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true}]}`,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"success with user id\",\n\t\t\tbody:               `{\"id\": \"98a46ea2-51f6-4e30-bd29-8272de77c8c8\", \"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true}]}`,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"success with multiple emails\",\n\t\t\tbody:               `{\"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true}, {\"address\": \"test2@test.com\"}]}`,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"success with created_at\",\n\t\t\tbody:               `{\"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true}], \"created_at\": \"2023-06-07T13:42:49.369489Z\"}`,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"with already existing user id\",\n\t\t\tbody:               `{\"id\": \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\", \"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true}]}`,\n\t\t\texpectedStatusCode: http.StatusConflict,\n\t\t},\n\t\t{\n\t\t\tname:               \"with non uuid v4 id\",\n\t\t\tbody:               `{\"id\": \"customId\", \"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true}]}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"with no emails\",\n\t\t\tbody:               `{\"id\": \"98a46ea2-51f6-4e30-bd29-8272de77c8c8\", \"emails\": []}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"with missing emails\",\n\t\t\tbody:               `{\"id\": \"98a46ea2-51f6-4e30-bd29-8272de77c8c8\"}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"with no primary email\",\n\t\t\tbody:               `{\"emails\": [{\"address\": \"test@test.com\"}]}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"with multiple primary emails\",\n\t\t\tbody:               `{\"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true\"}, {\"address\": \"test2@test.com\", \"is_primary\": true\"}]}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"with non unique emails\",\n\t\t\tbody:               `{\"emails\": [{\"address\": \"test@test.com\", \"is_primary\": true\"}, {\"address\": \"test@test.com\"}]}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"with already existing email\",\n\t\t\tbody:               `{\"emails\": [{\"address\": \"john.doe@example.com\", \"is_primary\": true}]}`,\n\t\t\texpectedStatusCode: http.StatusConflict,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.Require().NoError(s.Storage.MigrateUp())\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, \"/users\", strings.NewReader(currentTest.body))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\terr = e.Close()\n\t\t\ts.Require().NoError(err)\n\n\t\t\ts.Require().NoError(s.Storage.MigrateDown(-1))\n\t\t})\n\t}\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_Patch_Success() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserID             string\n\t\tbody               string\n\t\texpectedStatusCode int\n\t\tverify             func(s *userAdminSuite, rec *httptest.ResponseRecorder)\n\t}{\n\t\t{\n\t\t\tname:               \"success all fields\",\n\t\t\tuserID:             \"6be8e0e7-05e6-4223-807b-06fe1d5e7b75\",\n\t\t\tbody:               `{\"username\": \"newusername\", \"name\": \"New Name\", \"given_name\": \"New\", \"family_name\": \"Name\", \"picture\": \"https://example.com/pic.png\"}`,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\tverify: func(s *userAdminSuite, rec *httptest.ResponseRecorder) {\n\t\t\t\tvar got map[string]any\n\t\t\t\terr := json.Unmarshal(rec.Body.Bytes(), &got)\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.NotNil(got[\"username\"])\n\t\t\t\tusername := got[\"username\"].(map[string]any)\n\t\t\t\ts.Equal(\"newusername\", username[\"username\"])\n\t\t\t\ts.Equal(\"New Name\", got[\"name\"])\n\t\t\t\ts.Equal(\"New\", got[\"given_name\"])\n\t\t\t\ts.Equal(\"Name\", got[\"family_name\"])\n\t\t\t\ts.Equal(\"https://example.com/pic.png\", got[\"picture\"])\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:               \"success clear fields\",\n\t\t\tuserID:             \"6be8e0e7-05e6-4223-807b-06fe1d5e7b75\",\n\t\t\tbody:               `{\"username\": null, \"name\": null, \"given_name\": null, \"family_name\": null, \"picture\": null}`,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\tverify: func(s *userAdminSuite, rec *httptest.ResponseRecorder) {\n\t\t\t\tvar got map[string]any\n\t\t\t\terr := json.Unmarshal(rec.Body.Bytes(), &got)\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Nil(got[\"username\"])\n\t\t\t\ts.Empty(got[\"name\"])\n\t\t\t\ts.Empty(got[\"given_name\"])\n\t\t\t\ts.Empty(got[\"family_name\"])\n\t\t\t\ts.Empty(got[\"picture\"])\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:               \"partial update (create username)\",\n\t\t\tuserID:             \"b3210fd8-c71f-4c9d-8123-a58f5f46a07f\",\n\t\t\tbody:               `{\"username\": \"baruser\"}`,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t\tverify: func(s *userAdminSuite, rec *httptest.ResponseRecorder) {\n\t\t\t\tvar got map[string]any\n\t\t\t\terr := json.Unmarshal(rec.Body.Bytes(), &got)\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.NotNil(got[\"username\"])\n\t\t\t\tusername := got[\"username\"].(map[string]any)\n\t\t\t\ts.Equal(\"baruser\", username[\"username\"])\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.Require().NoError(s.Storage.MigrateUp())\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPatch, fmt.Sprintf(\"/users/%s\", currentTest.userID), strings.NewReader(currentTest.body))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\tif currentTest.verify != nil {\n\t\t\t\tcurrentTest.verify(s, rec)\n\t\t\t}\n\n\t\t\terr = e.Close()\n\t\t\ts.Require().NoError(err)\n\n\t\t\ts.Require().NoError(s.Storage.MigrateDown(-1))\n\t\t})\n\t}\n}\n\nfunc (s *userAdminSuite) TestUserHandlerAdmin_Patch_Failure() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserID             string\n\t\tbody               string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"invalid username\",\n\t\t\tuserID:             \"6be8e0e7-05e6-4223-807b-06fe1d5e7b75\",\n\t\t\tbody:               `{\"username\": \"invalid username with spaces\"}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"invalid picture URL\",\n\t\t\tuserID:             \"6be8e0e7-05e6-4223-807b-06fe1d5e7b75\",\n\t\t\tbody:               `{\"picture\": \"not-a-url\"}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"empty string username\",\n\t\t\tuserID:             \"6be8e0e7-05e6-4223-807b-06fe1d5e7b75\",\n\t\t\tbody:               `{\"username\": \"\"}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"user not found\",\n\t\t\tuserID:             \"00000000-0000-0000-0000-000000000000\",\n\t\t\tbody:               `{\"name\": \"Does Not Matter\"}`,\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"invalid user id\",\n\t\t\tuserID:             \"invalid-uuid\",\n\t\t\tbody:               `{\"name\": \"Does Not Matter\"}`,\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.Require().NoError(s.Storage.MigrateUp())\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/user_admin\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPatch, fmt.Sprintf(\"/users/%s\", currentTest.userID), strings.NewReader(currentTest.body))\n\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\n\t\t\terr = e.Close()\n\t\t\ts.Require().NoError(err)\n\n\t\t\ts.Require().NoError(s.Storage.MigrateDown(-1))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/user_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t_ \"github.com/lib/pq\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n)\n\nfunc TestUserSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(userSuite))\n}\n\ntype userSuite struct {\n\ttest.Suite\n}\n\nfunc (s *userSuite) TestUserHandler_Create_TokenInCookie() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := test.DefaultConfig\n\te := NewPublicRouter(&cfg, s.Storage, nil, nil)\n\n\tbody := UserCreateBody{Email: \"jane.doe@example.com\"}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tuser := dto.CreateUserResponse{}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &user)\n\t\ts.NoError(err)\n\t\ts.False(user.UserID.IsNil())\n\n\t\tcount, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, \"\", \"\")\n\t\ts.NoError(err)\n\t\ts.Equal(1, count)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(body.Email)\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\n\t\ts.Empty(rec.Header().Get(\"X-Auth-Token\"))\n\t\tcookies := rec.Result().Cookies()\n\t\trec.Result().Cookies()\n\t\ts.NotEmpty(cookies)\n\t\tfor _, cookie := range cookies {\n\t\t\tif cookie.Name == \"hanko\" {\n\t\t\t\ts.Regexp(\".*\\\\..*\\\\..*\", cookie.Value)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_Create_TokenInHeader() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\tcfg := test.DefaultConfig\n\tcfg.Session.EnableAuthTokenHeader = true\n\te := NewPublicRouter(&cfg, s.Storage, nil, nil)\n\n\tbody := UserCreateBody{Email: \"jane.doe@example.com\"}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tuser := dto.CreateUserResponse{}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &user)\n\t\ts.NoError(err)\n\t\ts.False(user.UserID.IsNil())\n\n\t\tcount, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, \"\", \"\")\n\t\ts.NoError(err)\n\t\ts.Equal(1, count)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(body.Email)\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\n\t\ts.True(!slices.ContainsFunc(rec.Result().Cookies(), func(cookie *http.Cookie) bool {\n\t\t\treturn strings.EqualFold(cookie.Name, cfg.Session.Cookie.Name)\n\t\t}))\n\n\t\tresponseToken := rec.Header().Get(\"X-Auth-Token\")\n\t\ts.NotEmpty(responseToken)\n\t\ts.Regexp(\".*\\\\..*\\\\..*\", responseToken)\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_Create_CaseInsensitive() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tbody := UserCreateBody{Email: \"JANE.DOE@EXAMPLE.COM\"}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tuser := dto.CreateUserResponse{}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &user)\n\t\ts.NoError(err)\n\t\ts.False(user.UserID.IsNil())\n\n\t\tcount, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, \"\", \"\")\n\t\ts.NoError(err)\n\t\ts.Equal(1, count)\n\n\t\temail, err := s.Storage.GetEmailPersister().FindByAddress(strings.ToLower(body.Email))\n\t\ts.NoError(err)\n\t\ts.NotNil(email)\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_Create_UserExists() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tbody := UserCreateBody{Email: \"john.doe@example.com\"}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusConflict, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_Create_UserExists_CaseInsensitive() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tbody := UserCreateBody{Email: \"JOHN.DOE@EXAMPLE.COM\"}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusConflict, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_Create_InvalidEmail() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", strings.NewReader(`{\"email\": 123\"}`))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_Create_EmailMissing() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", strings.NewReader(`{\"bogus\": 123}`))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_Create_AccountCreationDisabled() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\ttestConfig := test.DefaultConfig\n\ttestConfig.Account.AllowSignup = false\n\te := NewPublicRouter(&testConfig, s.Storage, nil, nil)\n\n\tbody := UserCreateBody{Email: \"jane.doe@example.com\"}\n\tbodyJson, err := json.Marshal(body)\n\ts.NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/users\", bytes.NewReader(bodyJson))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusForbidden, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_Get() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s\", userId.String()), nil)\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Equal(rec.Code, http.StatusOK)\n\t\tuser := models.User{}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &user)\n\t\ts.NoError(err)\n\t\ts.Equal(userId.String(), user.ID.String())\n\t\ts.Equal(len(user.WebauthnCredentials), 0)\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_GetUserWithWebAuthnCredential() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_with_webauthn_credential\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s\", userId.String()), nil)\n\trec := httptest.NewRecorder()\n\treq.AddCookie(cookie)\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Equal(rec.Code, http.StatusOK)\n\t\tvar resp struct {\n\t\t\tID                  string `json:\"id\"`\n\t\t\tWebauthnCredentials []struct {\n\t\t\t\tID string `json:\"id\"`\n\t\t\t} `json:\"webauthn_credentials\"`\n\t\t}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &resp)\n\t\ts.Require().NoError(err)\n\t\ts.Equal(userId.String(), resp.ID)\n\t\ts.Len(resp.WebauthnCredentials, 2)\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_Get_InvalidUserId() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\terr := s.LoadFixtures(\"../test/fixtures/user\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/users/invalidUserId\", nil)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusForbidden, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_GetUserIdByEmail_InvalidEmail() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/user\", strings.NewReader(`{\"email\": \"123\"}`))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_GetUserIdByEmail_InvalidJson() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/user\", strings.NewReader(`\"email\": \"123}`))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_GetUserIdByEmail_UserNotFound() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/user\", strings.NewReader(`{\"email\": \"unknownAddress@example.com\"}`))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusNotFound, rec.Code)\n}\n\nfunc (s *userSuite) TestUserHandler_GetUserIdByEmail() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_with_webauthn_credential\")\n\ts.Require().NoError(err)\n\n\tuserId := \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\"\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/user\", strings.NewReader(`{\"email\": \"john.doe@example.com\"}`))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tresponse := struct {\n\t\t\tUserId   string `json:\"id\"`\n\t\t\tVerified bool   `json:\"verified\"`\n\t\t}{}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &response)\n\t\ts.NoError(err)\n\t\ts.Equal(userId, response.UserId)\n\t\ts.Equal(true, response.Verified)\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_GetUserIdByEmail_CaseInsensitive() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_with_webauthn_credential\")\n\ts.Require().NoError(err)\n\n\tuserId := \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\"\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/user\", strings.NewReader(`{\"email\": \"JOHN.DOE@EXAMPLE.COM\"}`))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tresponse := struct {\n\t\t\tUserId   string `json:\"id\"`\n\t\t\tVerified bool   `json:\"verified\"`\n\t\t}{}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &response)\n\t\ts.NoError(err)\n\t\ts.Equal(userId, response.UserId)\n\t\ts.Equal(true, response.Verified)\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_Me() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/user_with_webauthn_credential\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\n\tcfg := test.DefaultConfig\n\tcfg.Passkey.Enabled = true\n\tcfg.MFA.Enabled = true\n\tcfg.MFA.TOTP.Enabled = true\n\tcfg.MFA.SecurityKeys.Enabled = true\n\te := NewPublicRouter(&cfg, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/me\", nil)\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tresponse := dto.ProfileData{}\n\t\terr = json.Unmarshal(rec.Body.Bytes(), &response)\n\t\ts.NoError(err)\n\t\ts.Equal(userId, response.UserID)\n\t\ts.Len(response.Emails, 1)\n\t\ts.Equal(\"john.doe@example.com\", response.Emails[0].Address)\n\t\ts.True(response.Emails[0].IsVerified)\n\t\ts.Len(response.Passkeys, 1)\n\t\ts.Equal(\"P8fcQ6U8zxJRzhI0yuUCOxcA_UyAs0jbauO5ektj4SM\", response.Passkeys[0].ID)\n\t\ts.Len(response.SecurityKeys, 1)\n\t\ts.Equal(\"security-key-cred-id\", response.SecurityKeys[0].ID)\n\t\ts.False(response.MFAConfig.AuthAppSetUp)\n\t\ts.True(response.MFAConfig.TOTPEnabled)\n\t\ts.True(response.MFAConfig.SecurityKeysEnabled)\n\t\ts.NotNil(response.Username)\n\t\ts.Equal(\"johndoe\", response.Username.Username)\n\t\ts.Equal(\"John Doe\", response.Name)\n\t\ts.Equal(\"John\", response.GivenName)\n\t\ts.Equal(\"Doe\", response.FamilyName)\n\t\ts.Equal(\"https://example.com/john.jpg\", response.Picture)\n\t\ts.NotNil(response.Metadata)\n\t\ts.Contains(string(response.Metadata.Public), \"tester\")\n\t\ts.Contains(string(response.Metadata.Unsafe), \"debug\")\n\t\ts.NotContains(string(rec.Body.Bytes()), \"private_metadata\")\n\t\ts.NotContains(string(rec.Body.Bytes()), \"quota\")\n\t\ts.Len(response.Identities, 1)\n\t\ts.Equal(\"Google\", response.Identities[0].Provider)\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_Logout() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/user\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/logout\", nil)\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusNoContent, rec.Code) {\n\t\tcookie := rec.Header().Get(\"Set-Cookie\")\n\t\ts.NotEmpty(cookie)\n\n\t\tsplit := strings.Split(cookie, \";\")\n\t\ts.Equal(\"Max-Age=0\", strings.TrimSpace(split[2]))\n\t}\n}\n\nfunc (s *userSuite) TestUserHandler_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/user\")\n\ts.Require().NoError(err)\n\n\tuserId, _ := uuid.FromString(\"b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\")\n\tcfg := test.DefaultConfig\n\tcfg.Account.AllowDeletion = true\n\te := NewPublicRouter(&cfg, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodDelete, \"/user\", nil)\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusNoContent, rec.Code) {\n\t\tcookie := rec.Header().Get(\"Set-Cookie\")\n\t\ts.NotEmpty(cookie)\n\n\t\tsplit := strings.Split(cookie, \";\")\n\t\ts.Equal(\"Max-Age=0\", strings.TrimSpace(split[2]))\n\t}\n\n\tcount, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, \"\", \"\")\n\ts.NoError(err)\n\ts.Equal(0, count)\n}\n"
  },
  {
    "path": "backend/handler/utils.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"net/http\"\n)\n\nfunc loadDto[I any](ctx echo.Context) (*I, error) {\n\tvar adminDto I\n\terr := ctx.Bind(&adminDto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn nil, echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\terr = ctx.Validate(adminDto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn nil, echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\treturn &adminDto, nil\n}\n\nfunc storeSession(cfg *config.Config, persister persistence.Persister, userId uuid.UUID, rawToken jwt.Token, httpContext echo.Context, tx *pop.Connection) error {\n\tactiveSessions, err := persister.GetSessionPersisterWithConnection(tx).ListActive(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list active sessions: %w\", err)\n\t}\n\n\t// remove all server side sessions that exceed the limit\n\tif len(activeSessions) >= cfg.Session.Limit {\n\t\tfor i := cfg.Session.Limit - 1; i < len(activeSessions); i++ {\n\t\t\terr = persister.GetSessionPersisterWithConnection(tx).Delete(activeSessions[i])\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to remove latest session: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tsessionID, _ := rawToken.Get(\"session_id\")\n\n\texpirationTime := rawToken.Expiration()\n\tsessionModel := models.Session{\n\t\tID:        uuid.FromStringOrNil(sessionID.(string)),\n\t\tUserID:    userId,\n\t\tCreatedAt: rawToken.IssuedAt(),\n\t\tUpdatedAt: rawToken.IssuedAt(),\n\t\tExpiresAt: &expirationTime,\n\t\tLastUsed:  rawToken.IssuedAt(),\n\t}\n\n\tif cfg.Session.AcquireIPAddress {\n\t\tsessionModel.IpAddress = nulls.NewString(httpContext.RealIP())\n\t}\n\n\tif cfg.Session.AcquireUserAgent {\n\t\tsessionModel.UserAgent = nulls.NewString(httpContext.Request().UserAgent())\n\t}\n\n\terr = persister.GetSessionPersisterWithConnection(tx).Create(sessionModel)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store session: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/handler/webauthn.go",
    "content": "package handler\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/intern\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n)\n\ntype WebauthnHandler struct {\n\tpersister             persistence.Persister\n\twebauthn              *webauthn.WebAuthn\n\tsessionManager        session.Manager\n\tcfg                   *config.Config\n\tauditLogger           auditlog.Logger\n\tauthenticatorMetadata mapper.AuthenticatorMetadata\n}\n\nconst (\n\tGetUserFailureMessage               = \"failed to get user: %w\"\n\tCastSessionFailureMessage           = \"failed to cast session object\"\n\tCreateAuditLogFailureMessage        = \"failed to create audit log: %w\"\n\tUserNotFoundMessage                 = \"user not found\"\n\tSubjectParseFailureMessage          = \"failed to parse subject as uuid: %w\"\n\tGetWebauthnCredentialFailureMessage = \"failed to get webauthn credentials: %w\"\n\tStoredChallengeMismatchMessage      = \"Stored challenge and received challenge do not match\"\n\tUnknownUserMessage                  = \"unknown user\"\n)\n\n// NewWebauthnHandler creates a new handler which handles all webauthn related routes\nfunc NewWebauthnHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger, authenticatorMetadata mapper.AuthenticatorMetadata) (*WebauthnHandler, error) {\n\tf := false\n\twa, err := webauthn.New(&webauthn.Config{\n\t\tRPDisplayName:         cfg.Webauthn.RelyingParty.DisplayName,\n\t\tRPID:                  cfg.Webauthn.RelyingParty.Id,\n\t\tRPOrigins:             cfg.Webauthn.RelyingParty.Origins,\n\t\tAttestationPreference: protocol.PreferDirectAttestation,\n\t\tAuthenticatorSelection: protocol.AuthenticatorSelection{\n\t\t\tRequireResidentKey: &f,\n\t\t\tResidentKey:        protocol.ResidentKeyRequirementDiscouraged,\n\t\t\tUserVerification:   protocol.VerificationRequired,\n\t\t},\n\t\tDebug: false,\n\t\tTimeouts: webauthn.TimeoutsConfig{\n\t\t\tLogin: webauthn.TimeoutConfig{\n\t\t\t\tTimeout: time.Duration(cfg.Webauthn.Timeouts.Login) * time.Millisecond,\n\t\t\t\tEnforce: true,\n\t\t\t},\n\t\t\tRegistration: webauthn.TimeoutConfig{\n\t\t\t\tTimeout: time.Duration(cfg.Webauthn.Timeouts.Registration) * time.Millisecond,\n\t\t\t\tEnforce: true,\n\t\t\t},\n\t\t},\n\t})\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create webauthn instance: %w\", err)\n\t}\n\n\treturn &WebauthnHandler{\n\t\tpersister:             persister,\n\t\twebauthn:              wa,\n\t\tsessionManager:        sessionManager,\n\t\tcfg:                   cfg,\n\t\tauditLogger:           auditLogger,\n\t\tauthenticatorMetadata: authenticatorMetadata,\n\t}, nil\n}\n\n// BeginRegistration returns credential creation options for the WebAuthnAPI. It expects a valid session JWT in the request.\nfunc (h *WebauthnHandler) BeginRegistration(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(CastSessionFailureMessage)\n\t}\n\tuId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse userId from JWT subject:%w\", err)\n\t}\n\twebauthnUser, user, err := h.getWebauthnUser(h.persister.GetConnection(), uId)\n\tif err != nil {\n\t\treturn fmt.Errorf(GetUserFailureMessage, err)\n\t}\n\tif webauthnUser == nil {\n\t\terr = h.auditLogger.Create(c, models.AuditLogWebAuthnRegistrationInitFailed, nil, fmt.Errorf(\"unknown user\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t}\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, UserNotFoundMessage).SetInternal(errors.New(fmt.Sprintf(\"user %s not found \", uId)))\n\t}\n\n\tt := true\n\toptions, sessionData, err := h.webauthn.BeginRegistration(\n\t\twebauthnUser,\n\t\twebauthn.WithAuthenticatorSelection(protocol.AuthenticatorSelection{\n\t\t\tRequireResidentKey: &t,\n\t\t\tResidentKey:        protocol.ResidentKeyRequirementRequired,\n\t\t\tUserVerification:   protocol.UserVerificationRequirement(h.cfg.Passkey.UserVerification),\n\t\t}),\n\n\t\twebauthn.WithConveyancePreference(protocol.ConveyancePreference(h.cfg.Passkey.AttestationPreference)),\n\t\t// don't set the excludeCredentials list, so an already registered device can be re-registered\n\t)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create webauthn creation options: %w\", err)\n\t}\n\n\terr = h.persister.GetWebauthnSessionDataPersister().Create(*intern.WebauthnSessionDataToModel(sessionData, models.WebauthnOperationRegistration))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store creation options session data: %w\", err)\n\t}\n\n\terr = h.auditLogger.Create(c, models.AuditLogWebAuthnRegistrationInitSucceeded, user, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t}\n\n\treturn c.JSON(http.StatusOK, options)\n}\n\n// FinishRegistration validates the WebAuthnAPI response and associates the credential with the user. It expects a valid session JWT in the request.\n// The session JWT must be associated to the same user who requested the credential creation options.\nfunc (h *WebauthnHandler) FinishRegistration(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(CastSessionFailureMessage)\n\t}\n\trequest, err := protocol.ParseCredentialCreationResponse(c.Request())\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err.Error())\n\t}\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tsessionDataPersister := h.persister.GetWebauthnSessionDataPersisterWithConnection(tx)\n\t\tsessionData, err := sessionDataPersister.GetByChallenge(request.Response.CollectedClientData.Challenge)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get webauthn registration session data: %w\", err)\n\t\t}\n\n\t\tif sessionData != nil && sessionData.Operation != models.WebauthnOperationRegistration {\n\t\t\tsessionData = nil\n\t\t}\n\n\t\tif sessionData == nil {\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnRegistrationFinalFailed, nil, fmt.Errorf(\"received unkown challenge\"))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t}\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, StoredChallengeMismatchMessage).SetInternal(errors.New(\"sessionData not found\"))\n\t\t}\n\n\t\tif sessionToken.Subject() != sessionData.UserId.String() {\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnRegistrationFinalFailed, nil, fmt.Errorf(\"user session does not match sessionData subject\"))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t}\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, StoredChallengeMismatchMessage).SetInternal(errors.New(\"userId in webauthn.sessionData does not match user session\"))\n\t\t}\n\n\t\twebauthnUser, user, err := h.getWebauthnUser(tx, sessionData.UserId)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(GetUserFailureMessage, err)\n\t\t}\n\n\t\tif webauthnUser == nil {\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnRegistrationFinalFailed, nil, fmt.Errorf(UnknownUserMessage))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t}\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest).SetInternal(errors.New(UserNotFoundMessage))\n\t\t}\n\n\t\tcredential, err := h.webauthn.CreateCredential(webauthnUser, *intern.WebauthnSessionDataFromModel(sessionData), request)\n\t\tif err != nil {\n\t\t\terrorMessage := \"failed to validate attestation\"\n\t\t\terrorStatus := http.StatusBadRequest\n\t\t\t// Safari currently (v. 16.2) does not provide a UI in case of a (registration) ceremony\n\t\t\t// being performed with an authenticator NOT protected by e.g. a PIN. While Chromium based browsers do offer\n\t\t\t// a UI guiding through the setup of a PIN, Safari simply performs the ceremony without then setting the UV\n\t\t\t// flag even if it is required. In order to provide an appropriate error message to the frontend/user, we\n\t\t\t// need to return an error response distinguishable from other error cases. We use a dedicated/separate HTTP\n\t\t\t// status code because it seemed a bit more robust than forcing the frontend to check on a matching\n\t\t\t// (sub-)string in the error message in order to properly display the error.\n\t\t\tif err, ok := err.(*protocol.Error); ok && err.Type == protocol.ErrVerification.Type && strings.Contains(err.DevInfo, \"User verification\") {\n\t\t\t\terrorMessage = fmt.Sprintf(\"%s: %s: %s\", errorMessage, err.Details, err.DevInfo)\n\t\t\t\terrorStatus = http.StatusUnprocessableEntity\n\t\t\t}\n\t\t\taErr := h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnRegistrationFinalFailed, user, errors.New(errorMessage))\n\t\t\tif aErr != nil {\n\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, aErr)\n\t\t\t}\n\n\t\t\treturn echo.NewHTTPError(errorStatus, errorMessage).SetInternal(err)\n\t\t}\n\n\t\tbackupEligible := request.Response.AttestationObject.AuthData.Flags.HasBackupEligible()\n\t\tbackupState := request.Response.AttestationObject.AuthData.Flags.HasBackupState()\n\t\tmodel := intern.WebauthnCredentialToModel(credential, sessionData.UserId, backupEligible, backupState, false, h.authenticatorMetadata)\n\t\terr = h.persister.GetWebauthnCredentialPersisterWithConnection(tx).Create(*model)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store webauthn credential: %w\", err)\n\t\t}\n\n\t\terr = sessionDataPersister.Delete(*sessionData)\n\t\tif err != nil {\n\t\t\tc.Logger().Errorf(\"failed to delete attestation session data: %w\", err)\n\t\t}\n\n\t\terr = h.auditLogger.Create(c, models.AuditLogWebAuthnRegistrationFinalSucceeded, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t}\n\n\t\treturn c.JSON(http.StatusOK, map[string]string{\"credential_id\": model.ID, \"user_id\": webauthnUser.UserId.String()})\n\t})\n}\n\ntype BeginAuthenticationBody struct {\n\tUserID *string `json:\"user_id\" validate:\"uuid\"`\n}\n\n// BeginAuthentication returns credential assertion options for the WebAuthnAPI.\nfunc (h *WebauthnHandler) BeginAuthentication(c echo.Context) error {\n\tvar request BeginAuthenticationBody\n\n\tif err := (&echo.DefaultBinder{}).BindBody(c, &request); err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tvar options *protocol.CredentialAssertion\n\tvar sessionData *webauthn.SessionData\n\tvar user *models.User\n\tif request.UserID != nil {\n\t\t// non discoverable login initialization\n\t\tuserId, err := uuid.FromString(*request.UserID)\n\t\tif err != nil {\n\t\t\terr = h.auditLogger.Create(c, models.AuditLogWebAuthnAuthenticationInitFailed, nil, fmt.Errorf(\"user_id is not a uuid\"))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t}\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse UserID as uuid\").SetInternal(err)\n\t\t}\n\t\tvar webauthnUser *intern.WebauthnUser\n\t\twebauthnUser, user, err = h.getWebauthnUser(h.persister.GetConnection(), userId)\n\t\tif err != nil {\n\t\t\treturn echo.NewHTTPError(http.StatusInternalServerError).SetInternal(fmt.Errorf(GetUserFailureMessage, err))\n\t\t}\n\t\tif webauthnUser == nil {\n\t\t\terr = h.auditLogger.Create(c, models.AuditLogWebAuthnAuthenticationInitFailed, nil, fmt.Errorf(UnknownUserMessage))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t}\n\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, UserNotFoundMessage)\n\t\t}\n\n\t\tif len(webauthnUser.WebAuthnCredentials()) > 0 {\n\t\t\toptions, sessionData, err = h.webauthn.BeginLogin(\n\t\t\t\twebauthnUser,\n\t\t\t\twebauthn.WithUserVerification(protocol.UserVerificationRequirement(h.cfg.Passkey.UserVerification)),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create webauthn assertion options: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\tif options == nil && sessionData == nil {\n\t\tvar err error\n\t\toptions, sessionData, err = h.webauthn.BeginDiscoverableLogin(\n\t\t\twebauthn.WithUserVerification(protocol.UserVerificationRequirement(h.cfg.Passkey.UserVerification)),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create webauthn assertion options for discoverable login: %w\", err)\n\t\t}\n\t}\n\n\terr := h.persister.GetWebauthnSessionDataPersister().Create(*intern.WebauthnSessionDataToModel(sessionData, models.WebauthnOperationAuthentication))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store webauthn assertion session data: %w\", err)\n\t}\n\n\t// Remove all transports, because of a bug in android and windows where the internal authenticator gets triggered,\n\t// when the transports array contains the type 'internal' although the credential is not available on the device.\n\tfor i := range options.Response.AllowedCredentials {\n\t\toptions.Response.AllowedCredentials[i].Transport = nil\n\t}\n\n\terr = h.auditLogger.Create(c, models.AuditLogWebAuthnAuthenticationInitSucceeded, user, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t}\n\n\treturn c.JSON(http.StatusOK, options)\n}\n\n// FinishAuthentication validates the WebAuthnAPI response and on success it returns a new session JWT.\nfunc (h *WebauthnHandler) FinishAuthentication(c echo.Context) error {\n\trequest, err := protocol.ParseCredentialRequestResponse(c.Request())\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err.Error())\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\tsessionDataPersister := h.persister.GetWebauthnSessionDataPersisterWithConnection(tx)\n\t\tsessionData, err := sessionDataPersister.GetByChallenge(request.Response.CollectedClientData.Challenge)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get webauthn assertion session data: %w\", err)\n\t\t}\n\n\t\tif sessionData != nil && sessionData.Operation != models.WebauthnOperationAuthentication {\n\t\t\tsessionData = nil\n\t\t}\n\n\t\tif sessionData == nil {\n\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnAuthenticationFinalFailed, nil, fmt.Errorf(\"received unkown challenge\"))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t}\n\t\t\treturn echo.NewHTTPError(http.StatusUnauthorized, StoredChallengeMismatchMessage).SetInternal(errors.New(\"sessionData not found\"))\n\t\t}\n\n\t\tmodel := intern.WebauthnSessionDataFromModel(sessionData)\n\n\t\tvar credential *webauthn.Credential\n\t\tvar webauthnUser *intern.WebauthnUser\n\t\tvar user *models.User\n\t\tif sessionData.UserId.IsNil() {\n\t\t\t// Discoverable Login\n\t\t\tuserId, err := uuid.FromBytes(request.Response.UserHandle)\n\t\t\tif err != nil {\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to parse userHandle as uuid\").SetInternal(err)\n\t\t\t}\n\t\t\twebauthnUser, user, err = h.getWebauthnUser(tx, userId)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(GetUserFailureMessage, err)\n\t\t\t}\n\n\t\t\tif webauthnUser == nil {\n\t\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnAuthenticationFinalFailed, nil, fmt.Errorf(UnknownUserMessage))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t\t}\n\t\t\t\treturn echo.NewHTTPError(http.StatusUnauthorized).SetInternal(errors.New(UserNotFoundMessage))\n\t\t\t}\n\n\t\t\tcredential, err = h.webauthn.ValidateDiscoverableLogin(func(rawID, userHandle []byte) (user webauthn.User, err error) {\n\t\t\t\treturn webauthnUser, nil\n\t\t\t}, *model, request)\n\t\t\tif err != nil {\n\t\t\t\tlogErr := h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnAuthenticationFinalFailed, user, fmt.Errorf(\"assertion validation failed\"))\n\t\t\t\tif logErr != nil {\n\t\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t\t}\n\t\t\t\treturn echo.NewHTTPError(http.StatusUnauthorized, \"failed to validate assertion\").SetInternal(err)\n\t\t\t}\n\t\t} else {\n\t\t\t// non discoverable Login\n\t\t\twebauthnUser, user, err = h.getWebauthnUser(tx, sessionData.UserId)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(GetUserFailureMessage, err)\n\t\t\t}\n\t\t\tif webauthnUser == nil {\n\t\t\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnAuthenticationFinalFailed, nil, fmt.Errorf(UnknownUserMessage))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t\t}\n\t\t\t\treturn echo.NewHTTPError(http.StatusUnauthorized).SetInternal(errors.New(UserNotFoundMessage))\n\t\t\t}\n\t\t\tcredential, err = h.webauthn.ValidateLogin(webauthnUser, *model, request)\n\t\t\tif err != nil {\n\t\t\t\tlogErr := h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnAuthenticationFinalFailed, user, fmt.Errorf(\"assertion validation failed\"))\n\t\t\t\tif logErr != nil {\n\t\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t\t\t}\n\t\t\t\treturn echo.NewHTTPError(http.StatusUnauthorized, \"failed to validate assertion\").SetInternal(err)\n\t\t\t}\n\t\t}\n\n\t\tvar dbCred *models.WebauthnCredential\n\t\tfor i := range webauthnUser.WebauthnCredentials {\n\t\t\tif webauthnUser.WebauthnCredentials[i].ID == base64.RawURLEncoding.EncodeToString(credential.ID) {\n\t\t\t\tdbCred = &webauthnUser.WebauthnCredentials[i]\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif dbCred != nil {\n\t\t\tif dbCred.BackupEligible != request.Response.AuthenticatorData.Flags.HasBackupEligible() || dbCred.BackupState != request.Response.AuthenticatorData.Flags.HasBackupState() {\n\t\t\t\tdbCred.BackupState = request.Response.AuthenticatorData.Flags.HasBackupState()\n\t\t\t\tdbCred.BackupEligible = request.Response.AuthenticatorData.Flags.HasBackupEligible()\n\t\t\t}\n\n\t\t\tnow := time.Now().UTC()\n\t\t\tdbCred.LastUsedAt = &now\n\n\t\t\tsignCount := int(request.Response.AuthenticatorData.Counter)\n\n\t\t\tif dbCred.SignCount > 0 && signCount > 0 && signCount <= dbCred.SignCount {\n\t\t\t\tsignCountErr := fmt.Errorf(\n\t\t\t\t\t\"signature counter mismatch: expected received signature count %d to be greater than current count %d\",\n\t\t\t\t\tsignCount,\n\t\t\t\t\tdbCred.SignCount,\n\t\t\t\t)\n\n\t\t\t\tlogErr := h.auditLogger.CreateWithConnection(\n\t\t\t\t\ttx,\n\t\t\t\t\tc,\n\t\t\t\t\tmodels.AuditLogWebAuthnAuthenticationFinalFailed,\n\t\t\t\t\tuser,\n\t\t\t\t\tfmt.Errorf(\"assertion validation failed\"),\n\t\t\t\t)\n\n\t\t\t\tif logErr != nil {\n\t\t\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, logErr)\n\t\t\t\t}\n\n\t\t\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"failed to validate assertion\").\n\t\t\t\t\tSetInternal(signCountErr)\n\t\t\t}\n\n\t\t\tdbCred.SignCount = signCount\n\n\t\t\terr = h.persister.GetWebauthnCredentialPersisterWithConnection(tx).Update(*dbCred)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to update webauthn credential: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\terr = sessionDataPersister.Delete(*sessionData)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete assertion session data: %w\", err)\n\t\t}\n\n\t\tvar emailJwt *dto.EmailJWT\n\t\tif e := user.Emails.GetPrimary(); e != nil {\n\t\t\temailJwt = dto.EmailJWTFromEmailModel(e)\n\t\t}\n\n\t\ttoken, rawToken, err := h.sessionManager.GenerateJWT(dto.UserJWT{\n\t\t\tUserID: user.ID.String(),\n\t\t\tEmail:  emailJwt,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to generate jwt: %w\", err)\n\t\t}\n\n\t\tcookie, err := h.sessionManager.GenerateCookie(token)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create session cookie: %w\", err)\n\t\t}\n\n\t\terr = storeSession(h.cfg, h.persister, webauthnUser.UserId, rawToken, c, tx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store session in DB: %w\", err)\n\t\t}\n\n\t\tc.Response().Header().Set(\"X-Session-Lifetime\", fmt.Sprintf(\"%d\", cookie.MaxAge))\n\n\t\tif h.cfg.Session.EnableAuthTokenHeader {\n\t\t\tc.Response().Header().Set(\"X-Auth-Token\", token)\n\t\t} else {\n\t\t\tc.SetCookie(cookie)\n\t\t}\n\n\t\terr = h.auditLogger.Create(c, models.AuditLogWebAuthnAuthenticationFinalSucceeded, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t}\n\n\t\treturn c.JSON(http.StatusOK, map[string]string{\"credential_id\": base64.RawURLEncoding.EncodeToString(credential.ID), \"user_id\": webauthnUser.UserId.String()})\n\t})\n}\n\nfunc (h *WebauthnHandler) ListCredentials(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(CastSessionFailureMessage)\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(SubjectParseFailureMessage, err)\n\t}\n\n\tcredentials, err := h.persister.GetWebauthnCredentialPersister().GetFromUser(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(GetWebauthnCredentialFailureMessage, err)\n\t}\n\n\tresponse := make([]*dto.WebauthnCredentialResponse, len(credentials))\n\n\tfor i := range credentials {\n\t\tresponse[i] = dto.FromWebauthnCredentialModel(&credentials[i])\n\t}\n\n\treturn c.JSON(http.StatusOK, response)\n}\n\nfunc (h *WebauthnHandler) UpdateCredential(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(CastSessionFailureMessage)\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(SubjectParseFailureMessage, err)\n\t}\n\n\tcredentialID := c.Param(\"id\")\n\n\tvar body dto.WebauthnCredentialUpdateRequest\n\n\terr = (&echo.DefaultBinder{}).BindBody(c, &body)\n\tif err != nil {\n\t\treturn dto.ToHttpError(err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(GetUserFailureMessage, err)\n\t}\n\n\tcredential, err := h.persister.GetWebauthnCredentialPersister().Get(credentialID)\n\tif err != nil {\n\t\treturn fmt.Errorf(GetWebauthnCredentialFailureMessage, err)\n\t}\n\n\tif credential == nil || credential.UserId.String() != user.ID.String() {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"the user does not have a webauthn credential with the specified credentialId\"))\n\t}\n\n\tif body.Name != nil {\n\t\tcredential.Name = body.Name\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\terr = h.persister.GetWebauthnCredentialPersisterWithConnection(tx).Update(*credential)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to update webauthn credential: %w\", err)\n\t\t}\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnCredentialUpdated, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (h *WebauthnHandler) DeleteCredential(c echo.Context) error {\n\tsessionToken, ok := c.Get(\"session\").(jwt.Token)\n\tif !ok {\n\t\treturn errors.New(CastSessionFailureMessage)\n\t}\n\n\tuserId, err := uuid.FromString(sessionToken.Subject())\n\tif err != nil {\n\t\treturn fmt.Errorf(SubjectParseFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch user from db: %w\", err)\n\t}\n\n\tcredentialId := c.Param(\"id\")\n\n\tcredential, err := h.persister.GetWebauthnCredentialPersister().Get(credentialId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get webauthn credential: %w\", err)\n\t}\n\n\tif credential == nil || credential.UserId.String() != user.ID.String() {\n\t\treturn echo.NewHTTPError(http.StatusNotFound).SetInternal(errors.New(\"the user does not have a webauthn credential with the specified credentialId\"))\n\t}\n\n\treturn h.persister.Transaction(func(tx *pop.Connection) error {\n\t\terr = h.persister.GetWebauthnCredentialPersisterWithConnection(tx).Delete(*credential)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete credential from db: %w\", err)\n\t\t}\n\n\t\terr = h.auditLogger.CreateWithConnection(tx, c, models.AuditLogWebAuthnCredentialDeleted, user, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(CreateAuditLogFailureMessage, err)\n\t\t}\n\n\t\treturn c.NoContent(http.StatusNoContent)\n\t})\n}\n\nfunc (h WebauthnHandler) getWebauthnUser(connection *pop.Connection, userId uuid.UUID) (*intern.WebauthnUser, *models.User, error) {\n\tuser, err := h.persister.GetUserPersisterWithConnection(connection).Get(userId)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(GetUserFailureMessage, err)\n\t}\n\n\tif user == nil {\n\t\treturn nil, nil, nil\n\t}\n\n\tcredentials, err := h.persister.GetWebauthnCredentialPersisterWithConnection(connection).GetFromUser(user.ID)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(GetWebauthnCredentialFailureMessage, err)\n\t}\n\n\twebauthnUser, err := intern.NewWebauthnUser(*user, credentials)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn webauthnUser, user, nil\n}\n"
  },
  {
    "path": "backend/handler/webauthn_credential_admin.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"net/http\"\n)\n\ntype WebauthnCredentialAdminHandler interface {\n\tList(ctx echo.Context) error\n\tGet(ctx echo.Context) error\n\tDelete(ctx echo.Context) error\n}\n\ntype webauthnCredentialAdminHandler struct {\n\tpersister persistence.Persister\n}\n\nfunc NewWebauthnCredentialAdminHandler(persister persistence.Persister) WebauthnCredentialAdminHandler {\n\treturn &webauthnCredentialAdminHandler{\n\t\tpersister: persister,\n\t}\n}\n\nfunc (h *webauthnCredentialAdminHandler) List(ctx echo.Context) error {\n\tlistDto, err := loadDto[admin.ListWebauthnCredentialsRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(listDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tcredentials, err := h.persister.GetWebauthnCredentialPersister().GetFromUser(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcredentialResponses := make([]dto.WebauthnCredentialResponse, len(credentials))\n\tfor i := range credentials {\n\t\tcredentialResponses[i] = *dto.FromWebauthnCredentialModel(&credentials[i])\n\t}\n\n\treturn ctx.JSON(http.StatusOK, credentialResponses)\n}\n\nfunc (h *webauthnCredentialAdminHandler) Get(ctx echo.Context) error {\n\tgetDto, err := loadDto[admin.GetWebauthnCredentialRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(getDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tcredential, err := h.persister.GetWebauthnCredentialPersister().Get(getDto.WebauthnCredentialID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif credential == nil || credential.UserId != userID {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"webauthn credential not found\")\n\t}\n\n\treturn ctx.JSON(http.StatusOK, dto.FromWebauthnCredentialModel(credential))\n}\n\nfunc (h *webauthnCredentialAdminHandler) Delete(ctx echo.Context) error {\n\tdeleteDto, err := loadDto[admin.GetWebauthnCredentialRequestDto](ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuserID, err := uuid.FromString(deleteDto.UserID)\n\tif err != nil {\n\t\treturn fmt.Errorf(parseUserUuidFailureMessage, err)\n\t}\n\n\tuser, err := h.persister.GetUserPersister().Get(userID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif user == nil {\n\t\treturn echo.NewHTTPError(http.StatusNotFound)\n\t}\n\n\tcredential, err := h.persister.GetWebauthnCredentialPersister().Get(deleteDto.WebauthnCredentialID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif credential == nil || credential.UserId != userID {\n\t\treturn echo.NewHTTPError(http.StatusNotFound, \"webauthn credential not found\")\n\t}\n\n\terr = h.persister.GetWebauthnCredentialPersister().Delete(*credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.NoContent(http.StatusNoContent)\n}\n"
  },
  {
    "path": "backend/handler/webauthn_credential_admin_test.go",
    "content": "package handler\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestWebauthnCredentialAdminSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(webauthnCredentialAdminSuite))\n}\n\ntype webauthnCredentialAdminSuite struct {\n\ttest.Suite\n}\n\nfunc (s *webauthnCredentialAdminSuite) TestWebauthnCredentialAdminHandler_List() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserID             string\n\t\texpectedCount      int\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should return webauthn credentials for user with multiple credentials\",\n\t\t\tuserID:             \"ec4ef049-5b88-4321-a173-21b0eff06a04\",\n\t\t\texpectedCount:      2,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"should return webauthn credentials for user with one credentials\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\texpectedCount:      1,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"should return webauthn credentials for user with no credentials\",\n\t\t\tuserID:             \"38bf5a00-d7ea-40a5-a5de-48722c148925\",\n\t\t\texpectedCount:      0,\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserID:             \"customUserId\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserID:             \"\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserID:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s/webauthn_credentials\", currentTest.userID), nil)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar credentials []dto.WebauthnCredentialResponse\n\t\t\t\terr = json.Unmarshal(rec.Body.Bytes(), &credentials)\n\t\t\t\ts.Require().NoError(err)\n\n\t\t\t\ts.Equal(len(credentials), currentTest.expectedCount)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *webauthnCredentialAdminSuite) TestWebauthnCredentialAdminHandler_Get() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserID             string\n\t\tcredentialID       string\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should return webauthn credential\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedStatusCode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if credential is not found\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tcredentialID:       \"notSoRandomCredentialID\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if credential is not associated to the user\",\n\t\t\tuserID:             \"ec4ef049-5b88-4321-a173-21b0eff06a04\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserID:             \"b5dd5267-b462-48be-b70d-bcd6f1bbe7a6\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserID:             \"\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty credentialID\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tcredentialID:       \"\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserID:             \"customUserId\",\n\t\t\tcredentialID:       \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/users/%s/webauthn_credentials/%s\", currentTest.userID, currentTest.credentialID), nil)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\tif http.StatusOK == rec.Code {\n\t\t\t\tvar credential dto.WebauthnCredentialResponse\n\t\t\t\terr = json.Unmarshal(rec.Body.Bytes(), &credential)\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Equal(currentTest.credentialID, credential.ID)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc (s *webauthnCredentialAdminSuite) TestWebauthnCredentialAdminHandler_Delete() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttests := []struct {\n\t\tname               string\n\t\tuserID             string\n\t\tcredentialID       string\n\t\texpectedCount      int\n\t\texpectedStatusCode int\n\t}{\n\t\t{\n\t\t\tname:               \"should delete webauthn credential for user with multiple credentials\",\n\t\t\tuserID:             \"ec4ef049-5b88-4321-a173-21b0eff06a04\",\n\t\t\tcredentialID:       \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\t\t\texpectedCount:      1,\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should delete webauthn credential for user with one credential\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedCount:      0,\n\t\t\texpectedStatusCode: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if credential is not found\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tcredentialID:       \"notSoRandomCredentialID\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail if credential is not associated to the user\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tcredentialID:       \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjK\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non existing user\",\n\t\t\tuserID:             \"30f41697-b413-43cc-8cca-d55298683607\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty userID\",\n\t\t\tuserID:             \"\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on empty credentialID\",\n\t\t\tuserID:             \"46626836-f2db-4ec0-8752-858b544cbc78\",\n\t\t\tcredentialID:       \"\",\n\t\t\texpectedStatusCode: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:               \"should fail on non uuid userID\",\n\t\t\tuserID:             \"customUserId\",\n\t\t\tcredentialID:       \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\t\t\texpectedStatusCode: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/users/%s/webauthn_credentials/%s\", currentTest.userID, currentTest.credentialID), nil)\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatusCode, rec.Code)\n\t\t\tif http.StatusNoContent == rec.Code {\n\t\t\t\tcredentials, err := s.Storage.GetWebauthnCredentialPersister().GetFromUser(uuid.FromStringOrNil(currentTest.userID))\n\t\t\t\ts.Require().NoError(err)\n\t\t\t\ts.Equal(currentTest.expectedCount, len(credentials))\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/webauthn_test.go",
    "content": "package handler\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n)\n\nfunc TestWebauthnSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(webauthnSuite))\n}\n\ntype webauthnSuite struct {\n\ttest.Suite\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_NewHandler() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\tmanager := getDefaultSessionManager(s.Storage)\n\thandler, err := NewWebauthnHandler(&test.DefaultConfig, s.Storage, manager, test.NewAuditLogger(), nil)\n\ts.NoError(err)\n\ts.NotEmpty(handler)\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_BeginRegistration() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"ec4ef049-5b88-4321-a173-21b0eff06a04\")\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/registration/initialize\", nil)\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tcreationOptions := protocol.CredentialCreation{}\n\t\terr = json.Unmarshal(rec.Body.Bytes(), &creationOptions)\n\t\ts.NoError(err)\n\n\t\tuId, err := base64.RawURLEncoding.DecodeString(creationOptions.Response.User.ID.(string))\n\t\ts.Require().NoError(err)\n\n\t\ts.NotEmpty(creationOptions.Response.Challenge)\n\t\ts.Equal(uuid.FromStringOrNil(userId.String()).Bytes(), uId)\n\t\ts.Equal(test.DefaultConfig.Webauthn.RelyingParty.Id, creationOptions.Response.RelyingParty.ID)\n\t\ts.Equal(protocol.ResidentKeyRequirementRequired, creationOptions.Response.AuthenticatorSelection.ResidentKey)\n\t\ts.Equal(protocol.VerificationPreferred, creationOptions.Response.AuthenticatorSelection.UserVerification)\n\t\ts.True(*creationOptions.Response.AuthenticatorSelection.RequireResidentKey)\n\t}\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_FinalizeRegistration() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn_registration\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"ec4ef049-5b88-4321-a173-21b0eff06a04\")\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\tbody := `{\n\"id\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"rawId\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"type\": \"public-key\",\n\"response\": {\n\"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjeSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFYmehnq3OAAI1vMYKZIsLJfHwVQMAWgGhXZHA-Erj4xfo8FKEcB_PmR7mOUVuOn7GZhLwV-kTSh2hrVc6QE7NOikFYXiDo2M_mJ3huHJkDnnc5dHtIxfedbpMdex5fY3hoFs-fwymQjtdqdvti5c4x6UBAgMmIAEhWCDxvVrRgK4vpnr6JxTx-KfpSNyQUtvc47ryryZmj-P5kSJYIDox8N9bHQBrxN-b5kXqfmj3GwAJW7nNCh8UPbus3B6I\",\n\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoidE9yTkRDRDJ4UWY0ekZqRWp3eGFQOGZPRXJQM3p6MDhyTW9UbEpHdG5LVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"\n}\n}`\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/registration/finalize\", strings.NewReader(body))\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Equal(`{\"credential_id\":\"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\"user_id\":\"ec4ef049-5b88-4321-a173-21b0eff06a04\"}`, strings.TrimSpace(rec.Body.String()))\n\t}\n\n\treq2 := httptest.NewRequest(http.MethodPost, \"/webauthn/registration/finalize\", strings.NewReader(body))\n\treq2.AddCookie(cookie)\n\trec2 := httptest.NewRecorder()\n\n\te.ServeHTTP(rec2, req2)\n\ts.Equal(http.StatusBadRequest, rec2.Code)\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_FinalizeRegistration_SessionDataExpired() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn_registration\")\n\ts.Require().NoError(err)\n\n\tuserId := uuid.FromStringOrNil(\"ec4ef049-5b88-4321-a173-21b0eff06a04\")\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tcookie, err := generateSessionCookie(s.Storage, userId)\n\ts.Require().NoError(err)\n\n\tbody := `{\n\"id\": \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\"rawId\": \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\"type\": \"public-key\",\n\"response\": {\n\"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAQECAwQFBgcIAQIDBAUGBwgAIOIlWRhTf45LVyZsJgZmkqtEK-E-tk9I1-z0u13IAFpTpQECAyYgASFYIAeA_nt5TQ8c7bc8hN9_3zqzp3coXO5aplEeHMOQG0hrIlggf_KVxZI_nIedc1XMrwwOMaYNd0qxVpFK7vU79fGBoxY\",\n\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiRmVNYzdzUjlFbGVod0VVNVR0RVdGaTdyUFAzLWtkWlhnbndMdGxiM0NoWSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODg4OCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"\n}\n}`\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/registration/finalize\", strings.NewReader(body))\n\treq.AddCookie(cookie)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_BeginAuthentication() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/login/initialize\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\tassertionOptions := protocol.CredentialAssertion{}\n\t\terr := json.Unmarshal(rec.Body.Bytes(), &assertionOptions)\n\t\ts.Require().NoError(err)\n\t\ts.NotEmpty(assertionOptions.Response.Challenge)\n\t\ts.Equal(assertionOptions.Response.UserVerification, protocol.VerificationPreferred)\n\t\ts.Equal(test.DefaultConfig.Webauthn.RelyingParty.Id, assertionOptions.Response.RelyingPartyID)\n\t}\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tbody := `{\n\"id\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"rawId\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"type\": \"public-key\",\n\"response\": {\n\"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFYmezOw\",\n\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZ0tKS21oOTB2T3BZTzU1b0hwcWFIWF9vTUNxNG9UWnQtRDBiNnRlSXpyRSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\"signature\": \"MEYCIQDi2vYVspG6pf38I4GyQCPOojGbvX4nwSPXCi0hm80twAIhAO3EWjhAnj0UpjU_l0AH5sEh3zq4LDvkvo3AUqaqfGYD\",\n\"userHandle\": \"7E7wSVuIQyGhcyGw7_BqBA\"\n}\n}`\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/login/finalize\", strings.NewReader(body))\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Empty(rec.Header().Get(\"X-Auth-Token\"))\n\t\tcookies := rec.Result().Cookies()\n\t\tif s.NotEmpty(cookies) {\n\t\t\tfor _, cookie := range cookies {\n\t\t\t\tif cookie.Name == \"hanko\" {\n\t\t\t\t\ts.Regexp(\".*\\\\..*\\\\..*\", cookie.Value) // check if cookie contains a jwt\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ts.Equal(`{\"credential_id\":\"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\"user_id\":\"ec4ef049-5b88-4321-a173-21b0eff06a04\"}`, strings.TrimSpace(rec.Body.String()))\n\t}\n\n\treq2 := httptest.NewRequest(http.MethodPost, \"/webauthn/login/finalize\", strings.NewReader(body))\n\trec2 := httptest.NewRecorder()\n\n\te.ServeHTTP(rec2, req2)\n\n\tif s.Equal(http.StatusUnauthorized, rec2.Code) {\n\t\thttpError := echo.HTTPError{}\n\t\terr = json.Unmarshal(rec2.Body.Bytes(), &httpError)\n\t\ts.NoError(err)\n\t\ts.Equal(\"Stored challenge and received challenge do not match\", httpError.Message)\n\t}\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication_SessionDataExpired() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tbody := `{\n\"id\": \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\"rawId\": \"4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\",\n\"type\": \"public-key\",\n\"response\": {\n\"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABA\",\n\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiQVVnTmtWOG5tVnd2LXJsOC1hSzFaRVg1RmxlbllDc2FUWTh2ZEVjSktKUSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODg4OCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9\",\n\"signature\": \"MEUCIQD46qneg2W9izFrJ-houyPia_QIcFR_9ZZIoWAqMywRmgIgMed7NAgcXkgCSWtVNknb_D70sn8b-fGwQQBlBCgIJ-c\",\n\"userHandle\": \"RmJoNvLbTsCHUoWLVEy8eA\"\n}\n}`\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/login/finalize\", strings.NewReader(body))\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusUnauthorized, rec.Code)\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication_TokenInHeader() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\tcfg := test.DefaultConfig\n\tcfg.Session.EnableAuthTokenHeader = true\n\te := NewPublicRouter(&cfg, s.Storage, nil, nil)\n\n\tbody := `{\n\"id\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"rawId\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"type\": \"public-key\",\n\"response\": {\n\"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFYmezOw\",\n\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZ0tKS21oOTB2T3BZTzU1b0hwcWFIWF9vTUNxNG9UWnQtRDBiNnRlSXpyRSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\"signature\": \"MEYCIQDi2vYVspG6pf38I4GyQCPOojGbvX4nwSPXCi0hm80twAIhAO3EWjhAnj0UpjU_l0AH5sEh3zq4LDvkvo3AUqaqfGYD\",\n\"userHandle\": \"7E7wSVuIQyGhcyGw7_BqBA\"\n}\n}`\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/login/finalize\", strings.NewReader(body))\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\tif s.Equal(http.StatusOK, rec.Code) {\n\t\ts.Empty(rec.Result().Cookies())\n\t\ttoken := rec.Header().Get(\"X-Auth-Token\")\n\t\ts.NotEmpty(token)\n\t\ts.Regexp(\".*\\\\..*\\\\..*\", token)\n\t\ts.Equal(`{\"credential_id\":\"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\"user_id\":\"ec4ef049-5b88-4321-a173-21b0eff06a04\"}`, strings.TrimSpace(rec.Body.String()))\n\t}\n\n\treq2 := httptest.NewRequest(http.MethodPost, \"/webauthn/login/finalize\", strings.NewReader(body))\n\trec2 := httptest.NewRecorder()\n\n\te.ServeHTTP(rec2, req2)\n\n\tif s.Equal(http.StatusUnauthorized, rec2.Code) {\n\t\thttpError := echo.HTTPError{}\n\t\terr = json.Unmarshal(rec2.Body.Bytes(), &httpError)\n\t\ts.NoError(err)\n\t\ts.Equal(\"Stored challenge and received challenge do not match\", httpError.Message)\n\t}\n}\n\nfunc (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication_SignCountMismatch() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\terr := s.LoadFixtures(\"../test/fixtures/webauthn\")\n\ts.Require().NoError(err)\n\n\tcredentialID := \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\"\n\tcredential, err := s.Storage.GetWebauthnCredentialPersister().Get(credentialID)\n\ts.Require().NoError(err)\n\ts.Require().NotNil(credential)\n\n\t// Set a higher sign count in the database to trigger a mismatch when the request arrives with a lower count (1650963259)\n\tcredential.SignCount = 1650963260\n\terr = s.Storage.GetWebauthnCredentialPersister().Update(*credential)\n\ts.Require().NoError(err)\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\tbody := `{\n\"id\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"rawId\": \"AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\",\n\"type\": \"public-key\",\n\"response\": {\n\"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFYmezOw\",\n\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZ0tKS21oOTB2T3BZTzU1b0hwcWFIWF9vTUNxNG9UWnQtRDBiNnRlSXpyRSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\"signature\": \"MEYCIQDi2vYVspG6pf38I4GyQCPOojGbvX4nwSPXCi0hm80twAIhAO3EWjhAnj0UpjU_l0AH5sEh3zq4LDvkvo3AUqaqfGYD\",\n\"userHandle\": \"7E7wSVuIQyGhcyGw7_BqBA\"\n}\n}`\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webauthn/login/finalize\", strings.NewReader(body))\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusBadRequest, rec.Code)\n\thttpError := echo.HTTPError{}\n\terr = json.Unmarshal(rec.Body.Bytes(), &httpError)\n\ts.NoError(err)\n\ts.Equal(\"failed to validate assertion\", httpError.Message)\n}\n\nvar userId = \"ec4ef049-5b88-4321-a173-21b0eff06a04\"\n\nvar uId, _ = uuid.FromString(userId)\n\nvar emails = []models.Email{\n\t{\n\t\tID:           uId,\n\t\tAddress:      \"john.doe@example.com\",\n\t\tPrimaryEmail: &models.PrimaryEmail{ID: uId},\n\t},\n}\n"
  },
  {
    "path": "backend/handler/webhook.go",
    "content": "package handler\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n)\n\ntype WebhookHandler interface {\n\tList(ctx echo.Context) error\n\tCreate(ctx echo.Context) error\n\tGet(ctx echo.Context) error\n\tDelete(ctx echo.Context) error\n\tUpdate(ctx echo.Context) error\n}\n\nconst (\n\tuuidErrorFormat = \"unable to create uuid: %w\"\n)\n\ntype webhookHandler struct {\n\tcfg       config.WebhookSettings\n\tpersister persistence.Persister\n}\n\nfunc NewWebhookHandler(cfg config.WebhookSettings, persister persistence.Persister) WebhookHandler {\n\treturn &webhookHandler{\n\t\tcfg:       cfg,\n\t\tpersister: persister,\n\t}\n}\n\nfunc (w *webhookHandler) List(ctx echo.Context) error {\n\tpersister := w.persister.GetWebhookPersister(nil)\n\tdbHooks, err := persister.List(true)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn fmt.Errorf(\"failed to list users: %w\", err)\n\t}\n\n\tlistDto := admin.WebhookListResponseDto{\n\t\tDatabase: dbHooks,\n\t\tConfig:   w.cfg.Hooks,\n\t}\n\n\treturn ctx.JSON(http.StatusOK, listDto)\n}\n\nfunc (w *webhookHandler) Create(ctx echo.Context) error {\n\tvar dto admin.CreateWebhookRequestDto\n\terr := ctx.Bind(&dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\terr = ctx.Validate(dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\tnow := time.Now()\n\n\tnewUuid, err := uuid.NewV4()\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf(uuidErrorFormat, err))\n\t}\n\n\tmodel := models.Webhook{\n\t\tID:            newUuid,\n\t\tCallback:      dto.Callback,\n\t\tEnabled:       true,\n\t\tFailures:      0,\n\t\tExpiresAt:     now.Add(webhooks.WebhookExpireDuration), // 30 Days from now\n\t\tWebhookEvents: nil,\n\t\tCreatedAt:     now,\n\t\tUpdatedAt:     now,\n\t}\n\n\tdbEvents, err := w.createWebhookEvents(dto.Events, model, now)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf(uuidErrorFormat, err))\n\t}\n\n\tpersister := w.persister.GetWebhookPersister(nil)\n\terr = persister.Create(model, dbEvents)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf(\"unable to save webhook: %w\", err))\n\t}\n\n\tmodel.WebhookEvents = dbEvents\n\n\treturn ctx.JSON(http.StatusCreated, model)\n}\n\nfunc (w *webhookHandler) createWebhookEvents(evts events.Events, webhook models.Webhook, now time.Time) (models.WebhookEvents, error) {\n\teventList := make(models.WebhookEvents, 0)\n\tfor _, event := range evts {\n\t\tnewUuid, err := uuid.NewV4()\n\t\tif err != nil {\n\t\t\treturn eventList, err\n\t\t}\n\n\t\tmodel := models.WebhookEvent{\n\t\t\tID:        newUuid,\n\t\t\tWebhook:   &webhook,\n\t\t\tEvent:     string(event),\n\t\t\tCreatedAt: now,\n\t\t\tUpdatedAt: now,\n\t\t}\n\n\t\teventList = append(eventList, model)\n\t}\n\n\treturn eventList, nil\n}\n\nfunc (w *webhookHandler) Get(ctx echo.Context) error {\n\tvar dto admin.GetWebhookRequestDto\n\terr := ctx.Bind(&dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\terr = ctx.Validate(dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\twebhookId, _ := uuid.FromString(dto.ID)\n\twebhook, err := w.getWebhook(webhookId, w.persister.GetWebhookPersister(nil))\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn err\n\t}\n\n\treturn ctx.JSON(http.StatusOK, webhook)\n}\n\nfunc (w *webhookHandler) Delete(ctx echo.Context) error {\n\tvar dto admin.GetWebhookRequestDto\n\terr := ctx.Bind(&dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\terr = ctx.Validate(dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\tpersister := w.persister.GetWebhookPersister(nil)\n\n\twebhookId, _ := uuid.FromString(dto.ID)\n\twebhook, err := w.getWebhook(webhookId, persister)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn err\n\t}\n\n\terr = persister.Delete(*webhook)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn fmt.Errorf(\"unable to delete webhook from database: %w\", err)\n\t}\n\n\treturn ctx.NoContent(http.StatusNoContent)\n}\n\nfunc (w *webhookHandler) Update(ctx echo.Context) error {\n\tvar dto admin.UpdateWebhookRequestDto\n\terr := ctx.Bind(&dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\terr = ctx.Validate(dto)\n\tif err != nil {\n\t\tctx.Logger().Error(err)\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, err)\n\t}\n\n\treturn w.persister.Transaction(func(tx *pop.Connection) error {\n\t\tpersister := w.persister.GetWebhookPersister(tx)\n\n\t\twebhookId, _ := uuid.FromString(dto.ID)\n\n\t\twebhook, err := w.getWebhook(webhookId, persister)\n\t\tif err != nil {\n\t\t\tctx.Logger().Error(err)\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, event := range webhook.WebhookEvents {\n\t\t\terr := persister.RemoveEvent(event)\n\t\t\tif err != nil {\n\t\t\t\tctx.Logger().Error(err)\n\t\t\t\treturn fmt.Errorf(\"unable to delete event: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tnow := time.Now()\n\t\tdbEvents, err := w.createWebhookEvents(dto.Events, *webhook, now)\n\t\tif err != nil {\n\t\t\tctx.Logger().Error(err)\n\t\t\treturn echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf(uuidErrorFormat, err))\n\t\t}\n\n\t\twebhook.WebhookEvents = dbEvents\n\t\twebhook.Callback = dto.Callback\n\t\twebhook.UpdatedAt = now\n\t\twebhook.Enabled = dto.Enabled\n\t\twebhook.Failures = 0\n\t\twebhook.ExpiresAt = now.Add(webhooks.WebhookExpireDuration)\n\n\t\terr = persister.Update(*webhook)\n\t\tif err != nil {\n\t\t\tctx.Logger().Error(err)\n\t\t\treturn fmt.Errorf(\"unable to update webhook: %w\", err)\n\t\t}\n\n\t\treturn ctx.JSON(http.StatusOK, webhook)\n\t})\n}\n\nfunc (w *webhookHandler) getWebhook(id uuid.UUID, persister persistence.WebhookPersister) (*models.Webhook, error) {\n\twebhook, err := persister.Get(id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to fetch webhook from database: %w\", err)\n\t}\n\n\tif webhook == nil {\n\t\treturn nil, echo.NewHTTPError(http.StatusNotFound, \"unable to find webhook with id: %s\", id.String())\n\t}\n\n\treturn webhook, nil\n}\n"
  },
  {
    "path": "backend/handler/webhook_test.go",
    "content": "package handler\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestWebhookHandlerSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(webhookSuite))\n}\n\ntype webhookSuite struct {\n\ttest.Suite\n}\n\nfunc (s *webhookSuite) TestWebhookHandler_List() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\ts.Require().NoError(err)\n\n\tcfg := test.DefaultConfig\n\n\tcfg.Webhooks = config.WebhookSettings{\n\t\tEnabled: true,\n\t\tHooks: config.Webhooks{\n\t\t\tconfig.Webhook{\n\t\t\t\tCallback: \"http://lorem\",\n\t\t\t\tEvents:   events.Events{events.UserDelete},\n\t\t\t},\n\t\t\tconfig.Webhook{\n\t\t\t\tCallback: \"http://ipsum\",\n\t\t\t\tEvents:   events.Events{events.UserCreate},\n\t\t\t},\n\t\t},\n\t}\n\n\te := NewAdminRouter(&cfg, s.Storage, nil)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/webhooks\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusOK, rec.Code)\n\n\tvar dto admin.WebhookListResponseDto\n\terr = json.Unmarshal(rec.Body.Bytes(), &dto)\n\n\ts.Require().NoError(err)\n\ts.Equal(5, len(dto.Database))\n\ts.Equal(2, len(dto.Config))\n}\n\nfunc (s *webhookSuite) TestWebhookHandler_Create() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttestBody := admin.CreateWebhookRequestDto{\n\t\tCallback: \"http://lorem\",\n\t\tEvents: events.Events{\n\t\t\tevents.UserDelete,\n\t\t},\n\t}\n\ttestBodyJson, err := json.Marshal(testBody)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/webhooks\", bytes.NewReader(testBodyJson))\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusCreated, rec.Code)\n\n\tresult := models.Webhook{}\n\terr = json.Unmarshal(rec.Body.Bytes(), &result)\n\n\ts.Require().NoError(err)\n\ts.Equal(testBody.Callback, result.Callback)\n\ts.Equal(string(testBody.Events[0]), result.WebhookEvents[0].Event)\n\ts.Equal(1, len(result.WebhookEvents))\n\ts.Require().NotNil(result.ID)\n\ts.Require().NotNil(result.WebhookEvents[0].ID)\n\n\terr = e.Close()\n\ts.Require().NoError(err)\n}\n\nfunc (s *webhookSuite) TestWebhookHandler_CreateWithParams() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname           string\n\t\tcallback       string\n\t\tevents         events.Events\n\t\texpectedStatus int\n\t}{\n\t\t{\n\t\t\tname:           \"success\",\n\t\t\tcallback:       \"http://lorem.ipsum\",\n\t\t\tevents:         events.Events{events.UserDelete},\n\t\t\texpectedStatus: http.StatusCreated,\n\t\t},\n\t\t{\n\t\t\tname:           \"empty callback\",\n\t\t\tcallback:       \"\",\n\t\t\tevents:         events.Events{events.UserDelete},\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"missing callback\",\n\t\t\tevents:         events.Events{events.UserDelete},\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"wrong callback\",\n\t\t\tcallback:       \"lorem\",\n\t\t\tevents:         events.Events{events.UserDelete},\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"empty events\",\n\t\t\tcallback:       \"http://lorem.ipsum\",\n\t\t\tevents:         events.Events{},\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"wrong event\",\n\t\t\tcallback:       \"http://lorem.ipsum\",\n\t\t\tevents:         events.Events{events.Event(\"cat\")},\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"missing event array\",\n\t\t\tcallback:       \"http://lorem.ipsum\",\n\t\t\tevents:         nil,\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.Require().NoError(s.Storage.MigrateUp())\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\ttestBody := admin.CreateWebhookRequestDto{\n\t\t\t\tCallback: currentTest.callback,\n\t\t\t\tEvents:   currentTest.events,\n\t\t\t}\n\t\t\ttestBodyJson, err := json.Marshal(testBody)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, \"/webhooks\", bytes.NewReader(testBodyJson))\n\t\t\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatus, rec.Code)\n\n\t\t\terr = e.Close()\n\t\t\ts.Require().NoError(err)\n\n\t\t\ts.Require().NoError(s.Storage.MigrateDown(-1))\n\t\t})\n\t}\n}\n\nfunc (s *webhookSuite) TestWebhookHandler_Delete() {\n\ts.Require().NoError(s.Storage.MigrateUp())\n\n\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttestId := \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\"\n\ttestUuid, err := uuid.FromString(testId)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/webhooks/%s\", testId), nil)\n\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusNoContent, rec.Code)\n\n\tpersister := s.Storage.GetWebhookPersister(nil)\n\n\tentry, err := persister.Get(testUuid)\n\ts.Require().NoError(err)\n\n\tlist, err := persister.List(true)\n\ts.Require().NoError(err)\n\n\ts.Require().Nil(entry)\n\ts.Equal(4, len(list))\n\n\terr = e.Close()\n\ts.Require().NoError(err)\n\n\ts.Require().NoError(s.Storage.MigrateDown(-1))\n}\n\nfunc (s *webhookSuite) TestWebhookHandler_DeleteWithParams() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname           string\n\t\ttestId         string\n\t\texpectedStatus int\n\t}{\n\t\t{\n\t\t\tname:           \"success\",\n\t\t\ttestId:         \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\texpectedStatus: http.StatusNoContent,\n\t\t},\n\t\t{\n\t\t\tname:           \"empty id\",\n\t\t\ttestId:         \"\",\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:           \"Non uuid\",\n\t\t\ttestId:         \"lorem\",\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"missing id\",\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.Require().NoError(s.Storage.MigrateUp())\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodDelete, fmt.Sprintf(\"/webhooks/%s\", currentTest.testId), nil)\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatus, rec.Code)\n\n\t\t\terr = e.Close()\n\t\t\ts.Require().NoError(err)\n\n\t\t\ts.Require().NoError(s.Storage.MigrateDown(-1))\n\n\t\t})\n\t}\n}\nfunc (s *webhookSuite) TestWebhookHandler_Get() {\n\ts.Require().NoError(s.Storage.MigrateUp())\n\n\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttestId := \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\"\n\n\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/webhooks/%s\", testId), nil)\n\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusOK, rec.Code)\n\n\tvar dto models.Webhook\n\terr = json.Unmarshal(rec.Body.Bytes(), &dto)\n\n\ts.Require().NotNil(dto)\n\ts.Equal(testId, dto.ID.String())\n\ts.Equal(\"http://localhost\", dto.Callback)\n\ts.Equal(false, dto.Enabled)\n\ts.Equal(3, dto.Failures)\n\n\terr = e.Close()\n\ts.Require().NoError(err)\n\n\ts.Require().NoError(s.Storage.MigrateDown(-1))\n}\n\nfunc (s *webhookSuite) TestWebhookHandler_GetWithParams() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname           string\n\t\ttestId         string\n\t\texpectedStatus int\n\t}{\n\t\t{\n\t\t\tname:           \"success\",\n\t\t\ttestId:         \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\texpectedStatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:           \"wrong ID\",\n\t\t\ttestId:         \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da7\",\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:           \"Non UUID ID\",\n\t\t\ttestId:         \"lorem\",\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"empty id\",\n\t\t\ttestId:         \"\",\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:           \"Non uuid\",\n\t\t\ttestId:         \"lorem\",\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"missing id\",\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.Require().NoError(s.Storage.MigrateUp())\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, fmt.Sprintf(\"/webhooks/%s\", currentTest.testId), nil)\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatus, rec.Code)\n\n\t\t\terr = e.Close()\n\t\t\ts.Require().NoError(err)\n\n\t\t\ts.Require().NoError(s.Storage.MigrateDown(-1))\n\n\t\t})\n\t}\n}\nfunc (s *webhookSuite) TestWebhookHandler_Update() {\n\ts.Require().NoError(s.Storage.MigrateUp())\n\n\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\ts.Require().NoError(err)\n\n\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\ttestId := \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\"\n\ttestUuid, err := uuid.FromString(testId)\n\ts.Require().NoError(err)\n\n\tupdateDto := admin.UpdateWebhookRequestDto{\n\t\tGetWebhookRequestDto: admin.GetWebhookRequestDto{\n\t\t\tID: testId,\n\t\t},\n\t\tCreateWebhookRequestDto: admin.CreateWebhookRequestDto{\n\t\t\tCallback: \"https://ipsum.magna/lorem\",\n\t\t\tEvents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t},\n\t\tEnabled: true,\n\t}\n\tupdateJson, err := json.Marshal(updateDto)\n\ts.Require().NoError(err)\n\n\treq := httptest.NewRequest(http.MethodPut, fmt.Sprintf(\"/webhooks/%s\", testId), bytes.NewReader(updateJson))\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusOK, rec.Code)\n\n\tvar result models.Webhook\n\terr = json.Unmarshal(rec.Body.Bytes(), &result)\n\n\t// Result Check\n\ts.Require().NotNil(result)\n\ts.Equal(testId, result.ID.String())\n\ts.Equal(updateDto.Callback, result.Callback)\n\ts.Equal(updateDto.Enabled, result.Enabled)\n\ts.Equal(0, result.Failures)\n\ts.Require().True(result.ExpiresAt.After(time.Now().Add(29 * 24 * time.Hour))) // 30 Days\n\ts.Require().True(result.CreatedAt.Before(result.UpdatedAt))\n\n\tdbHook, err := s.Storage.GetWebhookPersister(nil).Get(testUuid)\n\ts.Require().NoError(err)\n\ts.Equal(updateDto.Callback, dbHook.Callback)\n\ts.Equal(updateDto.Enabled, dbHook.Enabled)\n\ts.Equal(0, dbHook.Failures)\n\ts.Require().True(dbHook.ExpiresAt.After(time.Now().Add(29 * 24 * time.Hour))) // 30 Days\n\ts.Require().True(dbHook.CreatedAt.Before(dbHook.UpdatedAt))\n\n\terr = e.Close()\n\ts.Require().NoError(err)\n\n\ts.Require().NoError(s.Storage.MigrateDown(-1))\n}\n\nfunc (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode.\")\n\t}\n\n\ttests := []struct {\n\t\tname           string\n\t\ttestId         string\n\t\tcallback       string\n\t\tevents         events.Events\n\t\tenabled        bool\n\t\texpectedStatus int\n\t}{\n\t\t{\n\t\t\tname:     \"success\",\n\t\t\ttestId:   \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\tcallback: \"https://lorem.ipsum.et\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:     \"wrong ID\",\n\t\t\ttestId:   \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da7\",\n\t\t\tcallback: \"https://lorem.ipsum.et\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:     \"empty ID\",\n\t\t\ttestId:   \"\",\n\t\t\tcallback: \"https://lorem.ipsum.et\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:     \"non UUID ID\",\n\t\t\ttestId:   \"lorem\",\n\t\t\tcallback: \"https://lorem.ipsum.et\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:     \"missing ID\",\n\t\t\tcallback: \"https://lorem.ipsum.et\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:     \"empty Callback\",\n\t\t\ttestId:   \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\tcallback: \"\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:     \"wrong Callback\",\n\t\t\ttestId:   \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\tcallback: \"lorem\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:   \"missing Callback\",\n\t\t\ttestId: \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"empty events\",\n\t\t\ttestId:         \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\tcallback:       \"https://lorem.ipsum.et\",\n\t\t\tevents:         events.Events{},\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:           \"missing events\",\n\t\t\ttestId:         \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\tcallback:       \"https://lorem.ipsum.et\",\n\t\t\tenabled:        true,\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tname:     \"missing enable\",\n\t\t\ttestId:   \"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\",\n\t\t\tcallback: \"https://lorem.ipsum.et\",\n\t\t\tevents: events.Events{\n\t\t\t\tevents.UserDelete,\n\t\t\t},\n\t\t\texpectedStatus: http.StatusBadRequest,\n\t\t},\n\t}\n\n\tfor _, currentTest := range tests {\n\t\ts.Run(currentTest.name, func() {\n\t\t\ts.Require().NoError(s.Storage.MigrateUp())\n\n\t\t\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\t\t\ts.Require().NoError(err)\n\n\t\t\te := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)\n\n\t\t\tupdateDto := admin.UpdateWebhookRequestDto{\n\t\t\t\tGetWebhookRequestDto: admin.GetWebhookRequestDto{\n\t\t\t\t\tID: currentTest.testId,\n\t\t\t\t},\n\t\t\t\tCreateWebhookRequestDto: admin.CreateWebhookRequestDto{\n\t\t\t\t\tCallback: currentTest.callback,\n\t\t\t\t\tEvents:   currentTest.events,\n\t\t\t\t},\n\t\t\t\tEnabled: currentTest.enabled,\n\t\t\t}\n\t\t\tupdateJson, err := json.Marshal(updateDto)\n\t\t\ts.Require().NoError(err)\n\n\t\t\treq := httptest.NewRequest(http.MethodPut, fmt.Sprintf(\"/webhooks/%s\", currentTest.testId), bytes.NewReader(updateJson))\n\t\t\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\t\t\trec := httptest.NewRecorder()\n\n\t\t\te.ServeHTTP(rec, req)\n\n\t\t\ts.Equal(currentTest.expectedStatus, rec.Code)\n\n\t\t\terr = e.Close()\n\t\t\ts.Require().NoError(err)\n\n\t\t\ts.Require().NoError(s.Storage.MigrateDown(-1))\n\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/handler/well_known.go",
    "content": "package handler\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\thankoJwk \"github.com/teamhanko/hanko/backend/v2/crypto/jwk\"\n\tdto \"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"net/http\"\n)\n\ntype WellKnownHandler struct {\n\tjwkManager hankoJwk.Manager\n\tconfig     dto.PublicConfig\n}\n\nfunc NewWellKnownHandler(config config.Config, jwkManager hankoJwk.Manager) (*WellKnownHandler, error) {\n\treturn &WellKnownHandler{\n\t\tconfig:     dto.FromConfig(config),\n\t\tjwkManager: jwkManager,\n\t}, nil\n}\n\nfunc (h *WellKnownHandler) GetPublicKeys(c echo.Context) error {\n\tkeys, err := h.jwkManager.GetPublicKeys()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.Response().Header().Add(\"Cache-Control\", \"max-age=600\")\n\treturn c.JSON(http.StatusOK, keys)\n}\n\nfunc (h *WellKnownHandler) GetConfig(c echo.Context) error {\n\treturn c.JSON(http.StatusOK, h.config)\n}\n"
  },
  {
    "path": "backend/handler/well_known_test.go",
    "content": "package handler\n\nimport (\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestWellKnownSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(wellKnownSuite))\n}\n\ntype wellKnownSuite struct {\n\ttest.Suite\n}\n\nfunc (s *wellKnownSuite) TestWellKnownHandler_GetPublicKeys() {\n\tif testing.Short() {\n\t\ts.T().Skip(\"skipping test in short mode\")\n\t}\n\n\te := NewPublicRouter(&test.DefaultConfig, s.Storage, nil, nil)\n\n\treq := httptest.NewRequest(http.MethodGet, \"/.well-known/jwks.json\", nil)\n\trec := httptest.NewRecorder()\n\n\te.ServeHTTP(rec, req)\n\n\ts.Equal(http.StatusOK, rec.Code)\n\ts.Equal(\"max-age=600\", rec.Header().Get(\"Cache-Control\"))\n}\n"
  },
  {
    "path": "backend/json_schema/hanko.config.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://github.com/teamhanko/hanko/backend/v2/config/config\",\n  \"$ref\": \"#/$defs/Config\",\n  \"$defs\": {\n    \"Account\": {\n      \"properties\": {\n        \"allow_deletion\": {\n          \"type\": \"boolean\",\n          \"description\": \"`allow_deletion` determines whether users can delete their accounts.\",\n          \"default\": false\n        },\n        \"allow_signup\": {\n          \"type\": \"boolean\",\n          \"description\": \"`allow_signup` determines whether users are able to create new accounts.\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"AttributeMap\": {\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"default\": \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\"\n        },\n        \"family_name\": {\n          \"type\": \"string\",\n          \"default\": \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname\"\n        },\n        \"given_name\": {\n          \"type\": \"string\",\n          \"default\": \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname\"\n        },\n        \"middle_name\": {\n          \"type\": \"string\"\n        },\n        \"nickname\": {\n          \"type\": \"string\"\n        },\n        \"preferred_username\": {\n          \"type\": \"string\"\n        },\n        \"profile\": {\n          \"type\": \"string\"\n        },\n        \"picture\": {\n          \"type\": \"string\"\n        },\n        \"website\": {\n          \"type\": \"string\"\n        },\n        \"gender\": {\n          \"type\": \"string\"\n        },\n        \"birthdate\": {\n          \"type\": \"string\"\n        },\n        \"zone_info\": {\n          \"type\": \"string\"\n        },\n        \"locale\": {\n          \"type\": \"string\"\n        },\n        \"updated_at\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"default\": \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"\n        },\n        \"email_verified\": {\n          \"type\": \"string\"\n        },\n        \"phone\": {\n          \"type\": \"string\"\n        },\n        \"phone_verified\": {\n          \"type\": \"string\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"AuditLog\": {\n      \"properties\": {\n        \"console_output\": {\n          \"$ref\": \"#/$defs/AuditLogConsole\",\n          \"title\": \"console_output\",\n          \"description\": \"`console_output` controls audit log console output.\"\n        },\n        \"mask\": {\n          \"type\": \"boolean\",\n          \"description\": \"`mask` determines whether sensitive information (usernames, emails) should be masked in the audit log output.\\n\\nThis configuration applies to logs written to the console as well as persisted logs.\",\n          \"default\": true\n        },\n        \"storage\": {\n          \"$ref\": \"#/$defs/AuditLogStorage\",\n          \"description\": \"`storage` controls audit log retention.\"\n        },\n        \"retention\": {\n          \"type\": \"string\",\n          \"description\": \"`retention` specifies the time duration after which log audit entries may be deleted.\",\n          \"default\": \"720h\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"AuditLogConsole\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` controls whether audit log output on the console is enabled or disabled.\",\n          \"default\": true\n        },\n        \"output\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"stdout\",\n            \"stderr\"\n          ],\n          \"description\": \"`output` determines the output stream audit logs are sent to.\",\n          \"default\": \"stdout\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"AuditLogStorage\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` controls whether audit log should be retained (i.e. persisted).\",\n          \"default\": false\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Config\": {\n      \"properties\": {\n        \"account\": {\n          \"$ref\": \"#/$defs/Account\",\n          \"title\": \"account\",\n          \"description\": \"`account` configures settings related to user accounts.\"\n        },\n        \"audit_log\": {\n          \"$ref\": \"#/$defs/AuditLog\",\n          \"title\": \"audit_log\",\n          \"description\": \"`audit_log` configures output and storage modalities of audit logs.\"\n        },\n        \"convert_legacy_config\": {\n          \"type\": \"boolean\",\n          \"description\": \"`convert_legacy_config`, if set to `true`, automatically copies the set values of deprecated configuration\\noptions, to new ones. If set to `false`, these values have to be set manually if non-default values should be\\nused.\",\n          \"default\": false\n        },\n        \"convert_legacy_server_side_session_config\": {\n          \"type\": \"boolean\",\n          \"description\": \"`covert_legacy_session_config`, if set to `true`, automatically copies the set of deprecated server-side session\\nconfiguration options to the new ones. If set to `false`, these values have to be set manually if non-default\\nvalues should be used.\",\n          \"default\": true\n        },\n        \"database\": {\n          \"$ref\": \"#/$defs/Database\",\n          \"title\": \"database\",\n          \"description\": \"`database` configures database connection settings.\"\n        },\n        \"debug\": {\n          \"type\": \"boolean\",\n          \"description\": \"`debug`, if set to `true`, adds additional debugging information to flow API responses.\",\n          \"default\": false\n        },\n        \"email\": {\n          \"$ref\": \"#/$defs/Email\",\n          \"title\": \"email\",\n          \"description\": \"`email` configures how email addresses of user accounts are acquired and used.\"\n        },\n        \"email_delivery\": {\n          \"$ref\": \"#/$defs/EmailDelivery\",\n          \"title\": \"email_delivery\",\n          \"description\": \"`email_delivery` configures how outgoing mails are delivered.\"\n        },\n        \"emails\": {\n          \"$ref\": \"#/$defs/Emails\",\n          \"title\": \"emails\",\n          \"description\": \"Deprecated. See child properties for suggested replacements.\"\n        },\n        \"flow_locker\": {\n          \"$ref\": \"#/$defs/FlowLocker\",\n          \"description\": \"`flow_locker` confgures flow locking\"\n        },\n        \"log\": {\n          \"$ref\": \"#/$defs/LoggerConfig\",\n          \"title\": \"log\",\n          \"description\": \"`log` configures application logging.\"\n        },\n        \"mfa\": {\n          \"$ref\": \"#/$defs/MFA\",\n          \"title\": \"mfa\",\n          \"description\": \"`mfa` configures how multi-factor-authentication behaves.\"\n        },\n        \"passcode\": {\n          \"$ref\": \"#/$defs/Passcode\",\n          \"title\": \"passcode\",\n          \"description\": \"Deprecated. See child properties for suggested replacements.\"\n        },\n        \"passkey\": {\n          \"$ref\": \"#/$defs/Passkey\",\n          \"title\": \"passkey\",\n          \"description\": \"`passkey` configures how passkeys  are acquired and used.\"\n        },\n        \"password\": {\n          \"$ref\": \"#/$defs/Password\",\n          \"title\": \"password\",\n          \"description\": \"`password` configures how passwords are acquired and used.\"\n        },\n        \"rate_limiter\": {\n          \"$ref\": \"#/$defs/RateLimiter\",\n          \"title\": \"rate_limiter\",\n          \"description\": \"`rate_limiter` configures rate limits for rate limited API operations and storage modalities for rate limit data.\"\n        },\n        \"saml\": {\n          \"$ref\": \"#/$defs/Saml\",\n          \"title\": \"saml\",\n          \"description\": \"`saml` configures modalities of SAML (Security Assertion Markup Language) SSO authentication and SAML identity\\nproviders.\"\n        },\n        \"secrets\": {\n          \"$ref\": \"#/$defs/Secrets\",\n          \"title\": \"secrets\",\n          \"description\": \"`secrets` configures the keys used for cryptographically signing tokens issued by the API.\"\n        },\n        \"security_notifications\": {\n          \"$ref\": \"#/$defs/SecurityNotifications\",\n          \"description\": \"`security_notifications` configures security notifications for important security-related events.\"\n        },\n        \"server\": {\n          \"$ref\": \"#/$defs/Server\",\n          \"title\": \"server\",\n          \"description\": \"`server` configures address and CORS settings of the public and admin API.\"\n        },\n        \"service\": {\n          \"$ref\": \"#/$defs/Service\",\n          \"title\": \"service\",\n          \"description\": \"`service` configures general service information.\"\n        },\n        \"session\": {\n          \"$ref\": \"#/$defs/Session\",\n          \"title\": \"session\",\n          \"description\": \"`session` configures settings for session JWTs and Cookies issued by the API.\"\n        },\n        \"smtp\": {\n          \"$ref\": \"#/$defs/SMTP\",\n          \"title\": \"smtp\",\n          \"description\": \"Deprecated. Use `email_delivery.smtp` instead.\"\n        },\n        \"third_party\": {\n          \"$ref\": \"#/$defs/ThirdParty\",\n          \"title\": \"third_party\",\n          \"description\": \"`third_party` configures the modalities of third party OAuth/OIDC based authentication and available identity\\nproviders.\"\n        },\n        \"username\": {\n          \"$ref\": \"#/$defs/Username\",\n          \"title\": \"username\",\n          \"description\": \"`username` configures how usernames of user accounts are acquired and used.\"\n        },\n        \"webauthn\": {\n          \"$ref\": \"#/$defs/WebauthnSettings\",\n          \"title\": \"webauthn\",\n          \"description\": \"`webauthn` configures general settings for communication with the WebAuthentication API.\"\n        },\n        \"webhooks\": {\n          \"$ref\": \"#/$defs/WebhookSettings\",\n          \"title\": \"webhooks\",\n          \"description\": \"`webhooks` configures HTTP-based callbacks for specific events occurring in the system.\"\n        },\n        \"privacy\": {\n          \"$ref\": \"#/$defs/Privacy\",\n          \"title\": \"privacy\",\n          \"description\": \"`privacy` configures privacy settings\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"description\": \"Config is the central configuration type\"\n    },\n    \"Cookie\": {\n      \"properties\": {\n        \"domain\": {\n          \"type\": \"string\",\n          \"description\": \"`domain` is the domain the cookie will be bound to. Works for subdomains, but not cross-domain.\\nSee the `session.enable_auth_token_header` configuration instead if the API and the client application run on\\ndifferent domains.\",\n          \"default\": \"hanko\"\n        },\n        \"http_only\": {\n          \"type\": \"boolean\",\n          \"description\": \"`http_only` determines whether cookies are HTTP only or accessible by Javascript.\",\n          \"default\": true\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"description\": \"`name` is the name of the cookie.\",\n          \"default\": \"hanko\"\n        },\n        \"retention\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"session\",\n            \"persistent\",\n            \"prompt\"\n          ],\n          \"description\": \"`retention` determines the retention behavior of authentication cookies.\",\n          \"default\": \"persistent\",\n          \"meta:enum\": {\n            \"persistent\": \"Issues a cookie that remains stored on the user's device until it reaches its expiration date.\",\n            \"prompt\": \"Allows the user to choose whether to stay signed in. If the user selects 'Stay signed in', a persistent cookie is issued; a session cookie otherwise.\",\n            \"session\": \"Issues a temporary cookie that lasts for the duration of the browser session.\"\n          }\n        },\n        \"same_site\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"strict\",\n            \"lax\",\n            \"none\"\n          ],\n          \"description\": \"`same_site` controls whether a cookie is sent with cross-site requests.\\nSee [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value) for\\nmore details.\",\n          \"default\": \"strict\"\n        },\n        \"secure\": {\n          \"type\": \"boolean\",\n          \"description\": \"`secure` indicates whether the cookie is sent to the server only when a request is made with the https: scheme\\n(except on localhost).\\n\\nNOTE: `secure` must be set to `false` when working on `localhost` and with the Safari browser because it does\\nnot store secure cookies on `localhost`.\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Cors\": {\n      \"properties\": {\n        \"allow_origins\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"title\": \"allow_origins\",\n          \"description\": \"`allow_origins` determines the value of the Access-Control-Allow-Origin\\nresponse header. This header defines a list of [origins](https://developer.mozilla.org/en-US/docs/Glossary/Origin)\\nthat may access the resource.\\n\\nThe wildcard characters `*` and `?` are supported and are converted to regex fragments `.*` and `.` accordingly.\",\n          \"default\": [\n            \"http://localhost:8888\"\n          ]\n        },\n        \"unsafe_wildcard_origin_allowed\": {\n          \"type\": \"boolean\",\n          \"title\": \"unsafe_wildcard_origin_allowed\",\n          \"description\": \"`unsafe_wildcard_origin_allowed` allows a wildcard `*` origin to be used with AllowCredentials\\nflag. In that case we consider any origin allowed and send it back to the client in an `Access-Control-Allow-Origin` header.\\n\\nThis is INSECURE and potentially leads to [cross-origin](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties)\\nattacks. See also https://github.com/labstack/echo/issues/2400 for discussion on the subject.\\n\\nOptional. Default value is `false`.\",\n          \"default\": false\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"CustomThirdPartyProvider\": {\n      \"if\": {\n        \"properties\": {\n          \"enabled\": {\n            \"anyOf\": [\n              {\n                \"const\": false\n              },\n              {\n                \"type\": \"null\"\n              }\n            ]\n          }\n        }\n      },\n      \"then\": {},\n      \"else\": {\n        \"if\": {\n          \"properties\": {\n            \"use_discovery\": {\n              \"anyOf\": [\n                {\n                  \"const\": false\n                },\n                {\n                  \"type\": \"null\"\n                }\n              ]\n            }\n          }\n        },\n        \"then\": {\n          \"required\": [\n            \"authorization_endpoint\",\n            \"token_endpoint\",\n            \"userinfo_endpoint\"\n          ]\n        },\n        \"else\": {\n          \"required\": [\n            \"issuer\"\n          ]\n        },\n        \"required\": [\n          \"display_name\",\n          \"client_id\",\n          \"secret\",\n          \"scopes\"\n        ]\n      },\n      \"properties\": {\n        \"acr_values\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"description\": \"`acr_values` is a list of strings that specifies the Authentication Context Class Reference values that the\\nAuthorization Server is being requested to use for processing this Authentication Request.\"\n        },\n        \"allow_linking\": {\n          \"type\": \"boolean\",\n          \"description\": \"`allow_linking` indicates whether existing accounts can be automatically linked with this provider.\\n\\nLinking is based on matching one of the email addresses of an existing user account with the (primary)\\nemail address of the third party provider account.\",\n          \"default\": false\n        },\n        \"attribute_mapping\": {\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"object\",\n          \"description\": \"`attribute_mapping` defines a map that associates a set of known standard OIDC conformant end-user claims\\n(the key of a map entry) at the Hanko backend to claims retrieved from a third party provider (the value of the\\nmap entry). This is primarily necessary if a non-OIDC provider is configured/used in which case it is probable\\nthat user data returned from the userinfo endpoint does not already conform to OIDC standard claims.\\n\\nExample: You configure an OAuth Provider (i.e. non-OIDC) and the provider's configured userinfo endpoint returns\\nan end-user's user ID at the provider not under a `sub` key in its JSON response but rather under a `user_id`\\nkey. You would then configure an attribute mapping as follows:\\n\\n```yaml\\nattribute_mapping:\\n sub: user_id\\n```\\n\\nSee https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims for a list of known standard claims\\nthat provider claims can be mapped into. Any other claims received from a provider are not discarded but are\\nretained internally in a `custom_claims` claim.\\n\\nMappings are one-to-one mappings, complex mappings (e.g. mapping concatenations of two claims) are not possible.\"\n        },\n        \"authorization_endpoint\": {\n          \"type\": \"string\",\n          \"description\": \"URL of the provider's authorization endpoint where the end-user is redirected to authenticate and grant consent for\\nan application to access their resources.\\n\\nRequired if `use_discovery` is false or omitted.\"\n        },\n        \"issuer\": {\n          \"type\": \"string\",\n          \"description\": \"`issuer` is the provider's issuer identifier. It should be a URL that uses the \\\"https\\\"\\n\\tscheme and has no query or fragment components.\\n\\nRequired if `use_discovery` is true.\"\n        },\n        \"client_id\": {\n          \"type\": \"string\",\n          \"description\": \"`client_id` is the ID of the OAuth/OIDC client. Must be obtained from the provider.\\n\\nRequired if the provider is `enabled`.\"\n        },\n        \"display_name\": {\n          \"type\": \"string\",\n          \"description\": \"`display_name` is the name of the provider that is intended to be shown to an end-user.\\n\\nRequired if the provider is `enabled`.\"\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` indicates if the provider is enabled or disabled.\",\n          \"default\": false\n        },\n        \"prompt\": {\n          \"type\": \"string\",\n          \"description\": \"`prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent.\\nPossible values are:\\n- login\\n- none\\n- consent\\n- select_account\\nPlease note that not all providers support all values. Check the corresponding docs of the provider for supported values.\"\n        },\n        \"scopes\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"description\": \"`scopes` is a list of scopes requested from the provider that specify the level of access an application has to\\na user's resources on a server, defining what actions the app can perform on behalf of the user.\\n\\nRequired if the provider is `enabled`.\"\n        },\n        \"secret\": {\n          \"type\": \"string\",\n          \"description\": \"`secret` is the client secret for the OAuth/OIDC client. Must be obtained from the provider.\\n\\nRequired if the provider is `enabled`.\"\n        },\n        \"token_endpoint\": {\n          \"type\": \"string\",\n          \"description\": \"URL of the provider's token endpoint URL where an application exchanges an authorization code for an access\\ntoken, which is used to authenticate API requests on behalf of the end-user.\\n\\nRequired if `use_discovery` is false or omitted.\"\n        },\n        \"use_discovery\": {\n          \"type\": \"boolean\",\n          \"description\": \"`use_discovery` determines if configuration information about an OpenID Connect (OIDC) provider, such as\\nendpoint URLs and supported features,should be automatically retrieved, from a well-known\\nURL (typically /.well-known/openid-configuration).\",\n          \"default\": true\n        },\n        \"userinfo_endpoint\": {\n          \"type\": \"string\",\n          \"description\": \"URL of the provider's endpoint that returns claims about an authenticated end-user.\\n\\nRequired if `use_discovery` is false or omitted.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"title\": \"custom_provider\"\n    },\n    \"CustomThirdPartyProviders\": {\n      \"additionalProperties\": {\n        \"$ref\": \"#/$defs/CustomThirdPartyProvider\"\n      },\n      \"type\": \"object\"\n    },\n    \"Database\": {\n      \"properties\": {\n        \"database\": {\n          \"type\": \"string\",\n          \"description\": \"`database` determines the name of the database schema to use.\",\n          \"default\": \"hanko\"\n        },\n        \"dialect\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"postgres\",\n            \"mysql\",\n            \"mariadb\",\n            \"cockroach\"\n          ],\n          \"description\": \"`dialect` is the name of the database system to use.\",\n          \"default\": \"postgres\"\n        },\n        \"host\": {\n          \"type\": \"string\",\n          \"description\": \"`host` is the host the database system is running on.\",\n          \"default\": \"localhost\"\n        },\n        \"password\": {\n          \"type\": \"string\",\n          \"description\": \"`password` is the password for the database user to use for connecting to the database.\",\n          \"default\": \"hanko\"\n        },\n        \"port\": {\n          \"type\": \"string\",\n          \"description\": \"`port` is the port the database system is running on.\",\n          \"default\": \"5432\"\n        },\n        \"url\": {\n          \"type\": \"string\",\n          \"description\": \"`url` is a datasource connection string. It can be used instead of the rest of the database configuration\\noptions. If this `url` is set then it is prioritized, i.e. the rest of the options, if set, have no effect.\\n\\nSchema: `dialect://username:password@host:port/database`\",\n          \"examples\": [\n            \"postgres://hanko:hanko@localhost:5432/hanko\"\n          ]\n        },\n        \"user\": {\n          \"type\": \"string\",\n          \"description\": \"`user` is the database user to use for connecting to the database.\",\n          \"default\": \"hanko\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Email\": {\n      \"properties\": {\n        \"acquire_on_login\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_on_login` determines whether users, provided that they do not already have registered an email,\\n\\tare prompted to provide an email on login.\",\n          \"default\": false\n        },\n        \"acquire_on_registration\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_on_registration` determines whether users are prompted to provide an email on registration.\",\n          \"default\": true\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether emails are enabled.\",\n          \"default\": true\n        },\n        \"limit\": {\n          \"type\": \"integer\",\n          \"description\": \"'limit' determines the maximum number of emails a user can register.\",\n          \"default\": 5\n        },\n        \"max_length\": {\n          \"type\": \"integer\",\n          \"description\": \"`max_length` specifies the maximum allowed length of an email address.\",\n          \"default\": 100\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"`optional` determines whether users must provide an email when prompted.\\nThere must always be at least one email address associated with an account. The primary email address cannot be\\ndeleted if emails are required (`optional`: false`).\",\n          \"default\": false\n        },\n        \"passcode_ttl\": {\n          \"type\": \"integer\",\n          \"description\": \"`passcode_ttl` specifies, in seconds, how long a passcode is valid for.\",\n          \"default\": 300\n        },\n        \"passcode_charset\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"numeric\",\n            \"alphanumeric\"\n          ],\n          \"description\": \"`passcode_charset` specifies the characters that can be used in passcodes.\\nE.g. `numeric` allows only numbers, `alphanumeric` allows both numbers and letters.\",\n          \"default\": \"numeric\"\n        },\n        \"require_verification\": {\n          \"type\": \"boolean\",\n          \"description\": \"`require_verification` determines whether newly created emails must be verified by providing a passcode sent\\nto respective address.\",\n          \"default\": true\n        },\n        \"use_as_login_identifier\": {\n          \"type\": \"boolean\",\n          \"description\": \"`use_as_login_identifier` determines whether emails can be used as an identifier on login.\",\n          \"default\": true\n        },\n        \"use_for_authentication\": {\n          \"type\": \"boolean\",\n          \"description\": \"`user_for_authentication` determines whether users can log in by providing an email address and subsequently\\nproviding a passcode sent to the given email address.\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"EmailDelivery\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether the API delivers emails.\\nDisable if you want to send the emails yourself. To do so you must subscribe to the `email.create` webhook event.\",\n          \"default\": true\n        },\n        \"from_address\": {\n          \"type\": \"string\",\n          \"description\": \"`from_address` configures the sender address of emails sent to users.\",\n          \"default\": \"noreply@hanko.io\"\n        },\n        \"from_name\": {\n          \"type\": \"string\",\n          \"description\": \"`from_name` configures the sender name of emails sent to users.\",\n          \"default\": \"Hanko\"\n        },\n        \"smtp\": {\n          \"$ref\": \"#/$defs/SMTP\",\n          \"title\": \"smtp\",\n          \"description\": \"`SMTP` contains the SMTP server settings for sending mails.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Emails\": {\n      \"properties\": {\n        \"require_verification\": {\n          \"type\": \"boolean\",\n          \"description\": \"Deprecated. Use `email.require_verification` instead.\",\n          \"default\": true\n        },\n        \"max_num_of_addresses\": {\n          \"type\": \"integer\",\n          \"description\": \"Deprecated. Use `email.limit` instead.\",\n          \"default\": 5\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Events\": {\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"type\": \"array\"\n    },\n    \"FlowLocker\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` controls whether flow locking is enabled\",\n          \"default\": true\n        },\n        \"store\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"in_memory\",\n            \"redis\"\n          ],\n          \"description\": \"`store` sets the backend for the flow locker\",\n          \"default\": \"in_memory\"\n        },\n        \"redis_config\": {\n          \"$ref\": \"#/$defs/RedisConfig\",\n          \"description\": \"`redis_config` configures connection to a redis instance\\nRequired if `store` is set to `redis`\"\n        },\n        \"ttl\": {\n          \"type\": \"string\",\n          \"description\": \"`ttl` is the lock timeout (for Redis only)\",\n          \"default\": \"30s\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"IdentityProvider\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` activates or deactivates the identity provider.\",\n          \"default\": false\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"description\": \"`name` is the name given for the identity provider.\"\n        },\n        \"domain\": {\n          \"type\": \"string\",\n          \"description\": \"At login the domain will be extracted from the users email address and then used to identify the idp to use.\\nThis tag defines for which domain the idp is used.\"\n        },\n        \"metadata_url\": {\n          \"type\": \"string\",\n          \"description\": \"`metadata_url` is the URL the API can retrieve IdP metadata from.\"\n        },\n        \"skip_email_verification\": {\n          \"type\": \"boolean\",\n          \"description\": \"`skip_email_verification` determines whether the check if the `email_verified` attribute in the IdP response\\nwill be skipped.\"\n        },\n        \"attribute_map\": {\n          \"$ref\": \"#/$defs/AttributeMap\",\n          \"title\": \"attribute_map\",\n          \"description\": \"`attribute_map` is a map of attributes used to map attributes in IdP response to custom attributes at\\nHanko.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"JWTTemplate\": {\n      \"properties\": {\n        \"claims\": {\n          \"type\": \"object\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"KeyManagement\": {\n      \"if\": {\n        \"properties\": {\n          \"type\": {\n            \"const\": \"aws_kms\"\n          }\n        }\n      },\n      \"then\": {\n        \"required\": [\n          \"key_id\",\n          \"region\"\n        ]\n      },\n      \"properties\": {\n        \"type\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"local\",\n            \"aws_kms\"\n          ],\n          \"description\": \"Type specifies the key management system to use. Supported values are 'local' (default) for local key storage\\nor 'aws_kms' for AWS Key Management Service.\\nWhen using 'aws_kms,' the AWS credentials must be set using the standard AWS credential chain (in order of precedence).\\n1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)\\n2. Shared credentials file (~/.aws/credentials)\\n3. Shared config file (~/.aws/config)\\n4. IAM role for Amazon EC2 (via instance metadata service)\\n5. IAM role for Amazon ECS (via container credentials)\\n6. IAM role for Amazon EKS (via service account token)\"\n        },\n        \"key_id\": {\n          \"type\": \"string\",\n          \"description\": \"KeyID is the AWS KMS key identifier (ARN or alias) used for signing operations.\\nRequired when Type is 'aws_kms'.\"\n        },\n        \"region\": {\n          \"type\": \"string\",\n          \"description\": \"Region is the AWS region where the KMS key is located.\\nRequired when Type is 'aws_kms'.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"LoggerConfig\": {\n      \"properties\": {\n        \"log_health_and_metrics\": {\n          \"type\": \"boolean\",\n          \"description\": \"`log_health_and_metrics` determines whether requests of the `/health` and `/metrics` endpoints are logged.\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"log_health_and_metrics\"\n      ]\n    },\n    \"MFA\": {\n      \"properties\": {\n        \"acquire_on_login\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_on_login` configures if users are prompted creating an MFA credential on login.\",\n          \"default\": false\n        },\n        \"acquire_on_registration\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_on_registration` configures if users are prompted creating an MFA credential on registration.\",\n          \"default\": true\n        },\n        \"device_trust_cookie_name\": {\n          \"type\": \"string\",\n          \"description\": \"`device_trust_cookie_name` is the name of the cookie used to store the token of a trusted device.\",\n          \"default\": \"hanko_device_token\"\n        },\n        \"device_trust_duration\": {\n          \"type\": \"string\",\n          \"description\": \"`device_trust_duration` configures the duration a device remains trusted after authentication; once expired, the\\nuser must reauthenticate with MFA.\",\n          \"default\": \"720h\"\n        },\n        \"device_trust_max_users_per_device\": {\n          \"type\": \"integer\",\n          \"description\": \"`device_trust_max_users_per_device` limits how many users can have device trust on a single device/browser.\\nOldest entries are removed when the limit is exceeded. This allows multiple users to trust the same device\\nwithout overwriting each other's trust tokens.\",\n          \"default\": 20\n        },\n        \"device_trust_policy\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"always\",\n            \"prompt\",\n            \"never\"\n          ],\n          \"description\": \"`device_trust_policy` determines the conditions under which a device or browser is considered trusted, allowing\\nMFA to be skipped for subsequent logins.\",\n          \"default\": \"prompt\",\n          \"meta:enum\": {\n            \"always\": \"Devices are trusted without user consent until the trust expires, so MFA is skipped during subsequent logins.\",\n            \"never\": \"Devices are considered untrusted, so MFA is required for each login.\",\n            \"prompt\": \"The user can choose to trust the current device to skip MFA for subsequent logins.\"\n          }\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether multi-factor-authentication is enabled.\",\n          \"default\": true\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"`optional` determines whether users must create an MFA credential when prompted. The MFA credential cannot be\\ndeleted if multi-factor-authentication is required (`optional: false`).\",\n          \"default\": true\n        },\n        \"security_keys\": {\n          \"$ref\": \"#/$defs/SecurityKeys\",\n          \"title\": \"security_keys\",\n          \"description\": \"`security_keys` configures security key settings for multi-factor-authentication\"\n        },\n        \"totp\": {\n          \"$ref\": \"#/$defs/TOTP\",\n          \"title\": \"totp\",\n          \"description\": \"`totp` configures the TOTP (Time-Based One-Time-Password) method for multi-factor-authentication.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"acquire_on_login\",\n        \"acquire_on_registration\",\n        \"device_trust_duration\",\n        \"enabled\",\n        \"optional\"\n      ]\n    },\n    \"Options\": {\n      \"properties\": {\n        \"sign_authn_requests\": {\n          \"type\": \"boolean\",\n          \"description\": \"`sign_authn_requests` determines whether initial requests should be signed.\",\n          \"default\": true\n        },\n        \"force_login\": {\n          \"type\": \"boolean\",\n          \"description\": \"`force_login` forces the IdP to always show a login (even if there is an active session with the IdP).\",\n          \"default\": false\n        },\n        \"validate_encryption_cert\": {\n          \"type\": \"boolean\",\n          \"description\": \"`validate_encryption_cert` determines whether the certificate used for the encryption of the IdP responses should\\nbe checked for validity.\",\n          \"default\": true\n        },\n        \"skip_signature_validation\": {\n          \"type\": \"boolean\",\n          \"description\": \"`skip_signature_validation` determines whether the validity check of an IdP response's signature\\nshould be skipped.\",\n          \"default\": false\n        },\n        \"allow_missing_attributes\": {\n          \"type\": \"boolean\",\n          \"description\": \"`allow_missing_attributes` determines whether missing attributes are allowed (e.g. the IdP specifies a phone\\nattribute in the metadata but does not send it with a SAML Assertion Response).\",\n          \"default\": false\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Passcode\": {\n      \"properties\": {\n        \"ttl\": {\n          \"type\": \"integer\",\n          \"description\": \"Deprecated. Use `email.passcode_ttl` instead.\",\n          \"default\": 300\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Passkey\": {\n      \"properties\": {\n        \"acquire_on_registration\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"always\",\n            \"conditional\",\n            \"never\"\n          ],\n          \"description\": \"`acquire_on_registration` configures how users are prompted creating a passkey on registration.\",\n          \"default\": \"always\",\n          \"meta:enum\": {\n            \"always\": \"Indicates that users are always prompted to create a passkey on registration.\",\n            \"conditional\": \"Indicates that users are prompted to create a passkey on registration as long as the user does\\n\\t\\t\\t\\t\\t\\tnot have a password.\\n\\n\\t\\t\\t\\t\\t\\tIf passwords are also conditionally acquired on registration, then users are given a choice as\\n\\t\\t\\t\\t\\t\\tto what type of credential to create.\",\n            \"never\": \"Indicates that users are never prompted to create a passkey on registration.\"\n          }\n        },\n        \"acquire_on_login\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"always\",\n            \"conditional\",\n            \"never\"\n          ],\n          \"description\": \"`acquire_on_login` configures how users are prompted creating a passkey on login.\",\n          \"default\": \"always\",\n          \"meta:enum\": {\n            \"always\": \"Indicates that users are always prompted to create a passkey on login\\n\\t\\t\\t\\t\\tprovided that they do not already have a passkey.\",\n            \"conditional\": \"Indicates that users are prompted to create a passkey on login provided that\\n\\t\\t\\t\\t\\t\\tthey do not already have a passkey and do not have a password.\\n\\n\\t\\t\\t\\t\\t\\tIf passkeys are also conditionally acquired on login then users are given a choice as to what\\n\\t\\t\\t\\t\\t\\ttype of credential to register.\",\n            \"never\": \"Indicates that users are never prompted to create a passkey on login.\"\n          }\n        },\n        \"attestation_preference\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"direct\",\n            \"indirect\",\n            \"none\"\n          ],\n          \"description\": \"`attestation_preference` is used to specify the preference regarding attestation conveyance during\\ncredential generation.\",\n          \"default\": \"direct\",\n          \"meta:enum\": {\n            \"direct\": \"Indicates that the Relying Party wants to receive the attestation statement as generated by\\n\\t\\t\\t\\t\\tthe authenticator.\",\n            \"indirect\": \"Indicates that the Relying Party prefers an attestation conveyance yielding verifiable\\n\\t\\t\\t\\t\\tattestation statements, but allows the client to decide how to obtain such attestation statements.\",\n            \"none\": \"Indicates that the Relying Party is not interested in authenticator attestation.\"\n          }\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether users can create or authenticate with passkeys.\",\n          \"default\": true\n        },\n        \"limit\": {\n          \"type\": \"integer\",\n          \"description\": \"`limit` defines the maximum number of passkeys a user can have.\",\n          \"default\": 10\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"`optional` determines whether users must create a passkey when prompted. The last remaining passkey cannot be\\ndeleted if passkeys are required (`optional: false`).\\n\\nIt also takes part in determining the order of password and passkey acquisition\\non login and registration (see also `acquire_on_login` and `acquire_on_registration`): if one credential type is\\nrequired (`optional: false`) then that one takes precedence, i.e. is acquired first.\",\n          \"default\": true\n        },\n        \"user_verification\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"required\",\n            \"preferred\",\n            \"discouraged\"\n          ],\n          \"description\": \"`user_verification` specifies the requirements regarding local authorization with an authenticator through\\n various authorization gesture modalities; for example, through a touch plus pin code,\\n password entry, or biometric recognition.\\n\\nThe setting applies to both WebAuthn registration and authentication ceremonies.\",\n          \"default\": \"preferred\",\n          \"meta:enum\": {\n            \"discouraged\": \"Indicates that no user verification should be performed.\",\n            \"preferred\": \"Indicates that user verification is preferred but will not fail the operation if no\\n\\t\\t\\t\\t\\t\\tuser verification was performed.\",\n            \"required\": \"Indicates that user verification is always required.\"\n          }\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Password\": {\n      \"properties\": {\n        \"acquire_on_registration\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"always\",\n            \"conditional\",\n            \"never\"\n          ],\n          \"description\": \"`acquire_on_registration` configures how users are prompted creating a password on registration.\",\n          \"default\": \"always\",\n          \"meta:enum\": {\n            \"always\": \"Indicates that users are always prompted to create a password on registration.\",\n            \"conditional\": \"Indicates that users are prompted to create a password on registration as long as the user does\\n\\t\\t\\t\\t\\t\\tnot have a passkey.\\n\\n\\t\\t\\t\\t\\t\\tIf passkeys are also conditionally acquired on registration, then users are given a choice as\\n\\t\\t\\t\\t\\t\\tto what type of credential to register.\",\n            \"never\": \"Indicates that users are never prompted to create a password on registration.\"\n          }\n        },\n        \"acquire_on_login\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"always\",\n            \"conditional\",\n            \"never\"\n          ],\n          \"description\": \"`acquire_on_login` configures how users are prompted creating a password on login.\",\n          \"default\": \"never\",\n          \"meta:enum\": {\n            \"always\": \"Indicates that users are always prompted to create a password on login\\n\\t\\t\\t\\t\\tprovided that they do not already have a password.\",\n            \"conditional\": \"Indicates that users are prompted to create a password on login provided that\\n\\t\\t\\t\\t\\t\\tthey do not already have a password and do not have a passkey.\\n\\n\\t\\t\\t\\t\\t\\tIf passkeys are also conditionally acquired on login then users are given a choice as to what\\n\\t\\t\\t\\t\\t\\ttype of credential to register.\",\n            \"never\": \"Indicates that users are never prompted to create a password on login.\"\n          }\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether passwords are enabled or disabled.\",\n          \"default\": true\n        },\n        \"min_length\": {\n          \"type\": \"integer\",\n          \"description\": \"`min_length` determines the minimum password length.\",\n          \"default\": 8\n        },\n        \"min_password_length\": {\n          \"type\": \"integer\",\n          \"description\": \"Deprecated. Use `min_length` instead.\",\n          \"default\": 8\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"`optional` determines whether users must set a password when prompted. The password cannot be deleted if\\npasswords are required (`optional: false`).\\n\\nIt also takes part in determining the order of password and passkey acquisition\\non login and registration (see also `acquire_on_login` and `acquire_on_registration`): if one credential type is\\nrequired (`optional: false`) then that one takes precedence, i.e. is acquired first.\",\n          \"default\": false\n        },\n        \"recovery\": {\n          \"type\": \"boolean\",\n          \"description\": \"`recovery` determines whether users can start a recovery process, e.g. in case of a forgotten password.\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Privacy\": {\n      \"properties\": {\n        \"show_account_existence_hints\": {\n          \"type\": \"boolean\",\n          \"description\": \"`show_account_existence_hints` determines whether the user should get a user-friendly response rather than a privacy protecting one. E.g. on sign-up, when enabled the user will get \\\"user already exists\\\" response.\\nIt only has an effect when emails are enabled.\",\n          \"default\": false\n        },\n        \"only_show_actual_login_methods\": {\n          \"type\": \"boolean\",\n          \"description\": \"`only_show_actual_login_methods` determines whether the user will only be prompted with his configured login methods.\\nIt only has an effect when emails are enabled, can be used for authentication and passwords are enabled.\",\n          \"default\": false\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"RateLimiter\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` controls whether rate limiting is enabled or disabled.\",\n          \"default\": true\n        },\n        \"store\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"in_memory\",\n            \"redis\"\n          ],\n          \"description\": \"`store` sets the store for the rate limiter. When you have multiple instances of Hanko running, it is recommended to use\\n the `redis` store because otherwise your instances each have their own states.\",\n          \"default\": \"in_memory\"\n        },\n        \"redis_config\": {\n          \"$ref\": \"#/$defs/RedisConfig\",\n          \"description\": \"`redis_config` configures connection to a redis instance.\\nRequired if `store` is set to `redis`\"\n        },\n        \"passcode_limits\": {\n          \"$ref\": \"#/$defs/RateLimits\",\n          \"description\": \"`passcode_limits` controls rate limits for passcode operations.\"\n        },\n        \"otp_limits\": {\n          \"$ref\": \"#/$defs/RateLimits\",\n          \"description\": \"`otp_limits` controls rate limits for OTP login attempts.\"\n        },\n        \"password_limits\": {\n          \"$ref\": \"#/$defs/RateLimits\",\n          \"description\": \"`password_limits` controls rate limits for password login operations.\"\n        },\n        \"token_limits\": {\n          \"$ref\": \"#/$defs/RateLimits\",\n          \"description\": \"`token_limits` controls rate limits for token exchange operations.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"RateLimits\": {\n      \"properties\": {\n        \"tokens\": {\n          \"type\": \"integer\",\n          \"description\": \"`tokens` determines how many operations/requests can occur in the given `interval`.\",\n          \"default\": 3\n        },\n        \"interval\": {\n          \"type\": \"string\",\n          \"description\": \"`interval` determines when to reset the token interval.\\nIt must be a (possibly signed) sequence of decimal\\nnumbers, each with optional fraction and a unit suffix, such as \\\"300ms\\\", \\\"-1.5h\\\" or \\\"2h45m\\\".\\nValid time units are \\\"ns\\\", \\\"us\\\" (or \\\"µs\\\"), \\\"ms\\\", \\\"s\\\", \\\"m\\\", \\\"h\\\".\",\n          \"default\": \"1m\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"tokens\",\n        \"interval\"\n      ]\n    },\n    \"RedisConfig\": {\n      \"properties\": {\n        \"address\": {\n          \"type\": \"string\",\n          \"description\": \"`address` is the address of the redis instance in the form of `host[:port][/database]`.\"\n        },\n        \"password\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"description\": \"`password` is the password for the redis instance.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"address\"\n      ]\n    },\n    \"RelyingParty\": {\n      \"properties\": {\n        \"display_name\": {\n          \"type\": \"string\",\n          \"description\": \"`display_name` is the service's name that some WebAuthn Authenticators will display to the user during registration\\nand authentication ceremonies.\",\n          \"default\": \"Hanko Authentication Service\"\n        },\n        \"id\": {\n          \"type\": \"string\",\n          \"description\": \"`id` is the [effective domain](https://html.spec.whatwg.org/multipage/browsers.html#concept-origin-effective-domain)\\nthe passkey/WebAuthn credentials will be bound to.\",\n          \"default\": \"localhost\"\n        },\n        \"origins\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"description\": \"`origins` is a list of origins for which passkeys/WebAuthn credentials will be accepted by the server. Must\\ninclude the protocol and can only be the effective domain, or a registrable domain suffix of the effective\\ndomain, as specified in the [`id`](#id). Except for `localhost`, the protocol **must** always be `https` for\\npasskeys/WebAuthn to work. IP Addresses will not work.\\n\\nFor an Android application the origin must be the base64 url encoded SHA256 fingerprint of the signing\\ncertificate.\",\n          \"default\": [\n            \"http://localhost:8888\"\n          ]\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"description\": \"RelyingParty webauthn settings for your application using hanko.\"\n    },\n    \"SMTP\": {\n      \"properties\": {\n        \"host\": {\n          \"type\": \"string\",\n          \"default\": \"localhost\"\n        },\n        \"port\": {\n          \"type\": \"string\",\n          \"default\": \"465\"\n        },\n        \"user\": {\n          \"type\": \"string\"\n        },\n        \"password\": {\n          \"type\": \"string\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"description\": \"SMTP Server Settings for sending passcodes\"\n    },\n    \"Saml\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether the SAML API endpoints are available.\",\n          \"default\": false\n        },\n        \"endpoint_url\": {\n          \"type\": \"string\",\n          \"description\": \"`endpoint` is URL at which the SAML endpoints like metadata, callback, etc. are available\\n(e.g. `{YOUR_BACKEND_INSTANCE}/api`).\\n\\nWill be provided as metadata for IdP.\"\n        },\n        \"audience_uri\": {\n          \"type\": \"string\",\n          \"description\": \"`audience_uri` determines the intended recipient or audience for the SAML Assertion.\"\n        },\n        \"default_redirect_url\": {\n          \"type\": \"string\",\n          \"description\": \"`default_redirect_url` is the URL to redirect to in case of errors or when no `allowed_redirect_url` is provided.\"\n        },\n        \"allowed_redirect_urls\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"description\": \"`allowed_redirect_urls` is a list of URLs the backend is allowed to redirect to after third party sign-in was\\nsuccessful.\\n\\nSupports wildcard matching through globbing. e.g. `https://*.example.com` will allow `https://foo.example.com`\\nand `https://bar.example.com` to be accepted.\\n\\nGlobbing is also supported for paths, e.g. `https://foo.example.com/*` will match `https://foo.example.com/page1`\\nand `https://foo.example.com/page2`.\\n\\nA double asterisk (`**`) acts as a \\\"super\\\"-wildcard/match-all.\\n\\nSee [here](https://pkg.go.dev/github.com/gobwas/glob#Compile) for more on globbinh.\"\n        },\n        \"options\": {\n          \"$ref\": \"#/$defs/Options\",\n          \"title\": \"options\",\n          \"description\": \"`options` allows setting optional features for service provider operations.\"\n        },\n        \"identity_providers\": {\n          \"items\": {\n            \"$ref\": \"#/$defs/IdentityProvider\"\n          },\n          \"type\": \"array\",\n          \"description\": \"`identity_providers` is a list of SAML identity providers.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Secrets\": {\n      \"if\": {\n        \"properties\": {\n          \"key_management\": {\n            \"properties\": {\n              \"type\": {\n                \"const\": \"local\"\n              }\n            }\n          }\n        }\n      },\n      \"then\": {\n        \"properties\": {\n          \"keys\": {\n            \"minItems\": 1\n          }\n        }\n      },\n      \"properties\": {\n        \"key_management\": {\n          \"$ref\": \"#/$defs/KeyManagement\",\n          \"description\": \"KeyManagement configures the key management system used for signing JWTs.\\nSupports 'local' (default) which uses the keys defined in the 'keys' field to encrypt a newly generated private\\nRSA key in the database, or 'aws_kms' which uses AWS Key Management Service for key signatures.\"\n        },\n        \"keys\": {\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16,\n            \"title\": \"keys\"\n          },\n          \"type\": \"array\",\n          \"description\": \"`keys` are used to en- and decrypt the JWKs which get used to sign the JWTs issued by the API.\\nFor every key a JWK is generated, encrypted with the key and persisted in the database.\\n\\nYou can use this list for key rotation: add a new key to the beginning of the list and the corresponding\\nJWK will then be used for signing JWTs. All tokens signed with the previous JWK(s) will still\\nbe valid until they expire. Removing a key from the list does not remove the corresponding\\ndatabase record. If you remove a key, you also have to remove the database record, otherwise\\napplication startup will fail.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"SecurityKeys\": {\n      \"properties\": {\n        \"attestation_preference\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"direct\",\n            \"indirect\",\n            \"none\"\n          ],\n          \"description\": \"`attestation_preference` is used to specify the preference regarding attestation conveyance during\\ncredential generation.\",\n          \"default\": \"direct\"\n        },\n        \"authenticator_attachment\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"platform\",\n            \"cross-platform\",\n            \"no_preference\"\n          ],\n          \"description\": \"`authenticator_attachment`  is used to specify the preference regarding authenticator attachment during credential registration.\",\n          \"default\": \"cross-platform\"\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether security keys are eligible for multi-factor-authentication.\",\n          \"default\": true\n        },\n        \"limit\": {\n          \"type\": \"integer\",\n          \"description\": \"'limit' determines the maximum number of security keys a user can register.\",\n          \"default\": 10\n        },\n        \"user_verification\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"required\",\n            \"preferred\",\n            \"discouraged\"\n          ],\n          \"description\": \"`user_verification` specifies the requirements regarding local authorization with an authenticator through\\n various authorization gesture modalities; for example, through a touch plus pin code,\\n password entry, or biometric recognition.\\n\\nThe setting applies to both WebAuthn registration and authentication ceremonies.\",\n          \"default\": \"discouraged\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"enabled\"\n      ]\n    },\n    \"SecurityNotificationConfiguration\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"SecurityNotificationTypes\": {\n      \"properties\": {\n        \"password_update\": {\n          \"$ref\": \"#/$defs/SecurityNotificationConfiguration\"\n        },\n        \"primary_email_update\": {\n          \"$ref\": \"#/$defs/SecurityNotificationConfiguration\"\n        },\n        \"email_create\": {\n          \"$ref\": \"#/$defs/SecurityNotificationConfiguration\"\n        },\n        \"email_delete\": {\n          \"$ref\": \"#/$defs/SecurityNotificationConfiguration\"\n        },\n        \"passkey_create\": {\n          \"$ref\": \"#/$defs/SecurityNotificationConfiguration\"\n        },\n        \"mfa_create\": {\n          \"$ref\": \"#/$defs/SecurityNotificationConfiguration\"\n        },\n        \"mfa_delete\": {\n          \"$ref\": \"#/$defs/SecurityNotificationConfiguration\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"SecurityNotifications\": {\n      \"properties\": {\n        \"notifications\": {\n          \"$ref\": \"#/$defs/SecurityNotificationTypes\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Server\": {\n      \"properties\": {\n        \"public\": {\n          \"$ref\": \"#/$defs/ServerSettings\",\n          \"title\": \"public\",\n          \"description\": \"`public` contains the server configuration for the public API.\"\n        },\n        \"admin\": {\n          \"$ref\": \"#/$defs/ServerSettings\",\n          \"title\": \"admin\",\n          \"description\": \"`admin` contains the server configuration for the admin API.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"ServerSettings\": {\n      \"properties\": {\n        \"address\": {\n          \"type\": \"string\",\n          \"description\": \"`address` is the address of the server to listen on in the form of host:port.\\n\\nSee [net.Dial](https://pkg.go.dev/net#Dial) for details of the address format.\"\n        },\n        \"cors\": {\n          \"$ref\": \"#/$defs/Cors\",\n          \"title\": \"cors\",\n          \"description\": \"`cors` contains configuration options regarding Cross-Origin-Resource-Sharing.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"ServerSide\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether server-side sessions are enabled.\\n\\nNOTE: When enabled the session endpoint must be used in order to check if a session is still valid.\",\n          \"default\": false\n        },\n        \"limit\": {\n          \"type\": \"integer\",\n          \"description\": \"`limit` determines the maximum number of server-side sessions a user can have. When the limit is exceeded,\\nolder sessions are invalidated.\",\n          \"default\": 100\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Service\": {\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"description\": \"`name` determines the name of the service.\\nThis value is used, e.g. in the subject header of outgoing emails.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Session\": {\n      \"properties\": {\n        \"allow_revocation\": {\n          \"type\": \"boolean\",\n          \"description\": \"`allow_revocation` allows users to revoke their own sessions.\",\n          \"default\": true\n        },\n        \"audience\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"description\": \"`audience` is a list of strings that identifies the recipients that the JWT is intended for.\\nThe audiences are placed in the `aud` claim of the JWT.\\nIf not set, it defaults to the value of the`webauthn.relying_party.id` configuration parameter.\"\n        },\n        \"acquire_ip_address\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_ip_address` stores the user's IP address in the database.\",\n          \"default\": true\n        },\n        \"acquire_user_agent\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_user_agent` stores the user's user agent in the database.\",\n          \"default\": true\n        },\n        \"cookie\": {\n          \"$ref\": \"#/$defs/Cookie\",\n          \"description\": \"`cookie` contains configuration for the session cookie issued on successful registration or login.\"\n        },\n        \"enable_auth_token_header\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enable_auth_token_header` determines whether a session token (JWT) is returned in an `X-Auth-Token`\\nheader after a successful authentication. This option should be set to `true` if API and client applications\\nrun on different domains.\",\n          \"default\": false\n        },\n        \"issuer\": {\n          \"type\": \"string\",\n          \"description\": \"`issuer` is a string that identifies the principal (human user, an organization, or a service)\\nthat issued the JWT. Its value is set in the `iss` claim of a JWT.\"\n        },\n        \"lifespan\": {\n          \"type\": \"string\",\n          \"description\": \"`lifespan` determines the maximum duration for which a session token (JWT) is valid. It must be a (possibly signed) sequence of decimal\\nnumbers, each with optional fraction and a unit suffix, such as \\\"300ms\\\", \\\"-1.5h\\\" or \\\"2h45m\\\".\\nValid time units are \\\"ns\\\", \\\"us\\\" (or \\\"µs\\\"), \\\"ms\\\", \\\"s\\\", \\\"m\\\", \\\"h\\\".\",\n          \"default\": \"12h\"\n        },\n        \"limit\": {\n          \"type\": \"integer\",\n          \"description\": \"`limit` determines the maximum number of server-side sessions a user can have. When the limit is exceeded,\\nolder sessions are invalidated.\",\n          \"default\": 5\n        },\n        \"show_on_profile\": {\n          \"type\": \"boolean\",\n          \"description\": \"`show_on_profile` indicates that the sessions should be listed on the profile.\",\n          \"default\": true\n        },\n        \"server_side\": {\n          \"$ref\": \"#/$defs/ServerSide\",\n          \"description\": \"Deprecated. Use settings in parent object.\\n`server_side` contains configuration for server-side sessions.\"\n        },\n        \"jwt_template\": {\n          \"$ref\": \"#/$defs/JWTTemplate\",\n          \"description\": \"`jwt_template` defines a template for adding custom `claims` to session JWTs.\\n\\nThese claims are processed at JWT generation time and can include static values,\\ntemplated strings using Go's text/template syntax, or nested structures (maps and slices).\\n\\nThe template has access to user data via the `.User` field, which includes:\\n- `.User.UserID`: The user's unique ID (string)\\n- `.User.Email`: Email details (optional, with `.Address`, `.IsPrimary`, `.IsVerified`)\\n- `.User.Username`: The user's username (string, optional)\\n\\nClaims that fail to process (e.g., due to invalid templates) are logged and skipped,\\nensuring JWT generation continues without interruption.\\n\\nExample usage in YAML configuration:\\n```yaml\\nsession:\\n  lifespan: 24h\\n  jwt_template:\\n    claims:\\n      role: \\\"user\\\"                               # Static value\\n      user_email: \\\"{{.User.Email.Address}}\\\"      # Templated string\\n      is_verified: \\\"{{.User.Email.IsVerified}}\\\"  # Boolean from user data\\n      metadata:                                  # Nested map\\n        source: \\\"hanko\\\"\\n        greeting: \\\"Hello {{.User.Username}}\\\"\\n      scopes:                                    # Slice with templated value\\n        - \\\"read\\\"\\n        - \\\"write\\\"\\n        - \\\"{{if .User.Email.IsVerified}}admin{{else}}basic{{end}}\\\"\\n```\\n\\nIn this example:\\n- `role` is a static string (\\\"user\\\").\\n- `user_email` dynamically inserts the user's email address.\\n- `is_verified` inserts a boolean indicating email verification status.\\n- `metadata` is a nested map with a static `source` and a templated `greeting`.\\n- `scopes` is a slice combining static values and a conditional template.\\n\\nNotes:\\n- Claims with the following keys will be ignored because they are currently added to the JWT\\n  by default:\\n    - sub\\n    - iat\\n    - exp\\n    - aud\\n    - iss\\n    - email\\n    - username\\n    - session_id\\n- Templates must be valid Go `text/template` syntax. Invalid templates are logged and ignored.\\n- Boolean strings (\\\"true\\\" or \\\"false\\\") from templates are automatically converted to actual booleans.\\n- Use conditionals (e.g., `{{if .User.Email}}`) to handle optional fields safely.\\n\\nFor more details on template syntax, see: https://pkg.go.dev/text/template\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"TOTP\": {\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether TOTP is eligible for multi-factor-authentication.\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"enabled\"\n      ]\n    },\n    \"ThirdParty\": {\n      \"if\": {\n        \"allOf\": [\n          {\n            \"properties\": {\n              \"providers\": {\n                \"patternProperties\": {\n                  \"^.*\": {\n                    \"$ref\": \"#/$defs/ThirdPartyProvider\",\n                    \"properties\": {\n                      \"enabled\": {\n                        \"anyOf\": [\n                          {\n                            \"const\": false\n                          },\n                          {\n                            \"const\": \"null\"\n                          }\n                        ]\n                      }\n                    }\n                  }\n                },\n                \"type\": \"object\"\n              }\n            }\n          },\n          {\n            \"properties\": {\n              \"custom_providers\": {\n                \"additionalProperties\": {\n                  \"$ref\": \"#/$defs/CustomThirdPartyProvider\",\n                  \"properties\": {\n                    \"enabled\": {\n                      \"anyOf\": [\n                        {\n                          \"const\": false\n                        },\n                        {\n                          \"type\": \"null\"\n                        }\n                      ]\n                    }\n                  }\n                }\n              }\n            }\n          }\n        ]\n      },\n      \"then\": {},\n      \"else\": {\n        \"required\": [\n          \"redirect_url\",\n          \"error_redirect_url\",\n          \"allowed_redirect_urls\"\n        ]\n      },\n      \"properties\": {\n        \"providers\": {\n          \"$ref\": \"#/$defs/ThirdPartyProviders\",\n          \"title\": \"providers\",\n          \"description\": \"`providers` contains the configurations for the available OAuth/OIDC identity providers.\"\n        },\n        \"custom_providers\": {\n          \"$ref\": \"#/$defs/CustomThirdPartyProviders\",\n          \"title\": \"custom_providers\",\n          \"description\": \"`custom_providers contains the configurations for custom OAuth/OIDC identity providers.\"\n        },\n        \"redirect_url\": {\n          \"type\": \"string\",\n          \"description\": \"`redirect_url` is the URL the third party provider redirects to with an authorization code. Must consist of the base URL\\nof your running Hanko backend instance and the `callback` endpoint of the API,\\ni.e. `{YOUR_BACKEND_INSTANCE}/thirdparty/callback.`\\n\\nRequired if any of the [`providers`](#providers) are `enabled`.\",\n          \"examples\": [\n            \"https://yourinstance.com/thirdparty/callback\"\n          ]\n        },\n        \"error_redirect_url\": {\n          \"type\": \"string\",\n          \"description\": \"`error_redirect_url` is the URL the backend redirects to if an error occurs during third party sign-in.\\nErrors are provided as 'error' and 'error_description' query params in the redirect location URL.\\n\\nWhen using the Hanko web components it should be the URL of the page that embeds the web component such that\\nerrors can be processed properly by the web component.\\n\\nYou do not have to add this URL to the 'allowed_redirect_urls', it is automatically included when validating\\nredirect URLs.\\n\\nRequired if any of the [`providers`](#providers) are `enabled`. Must not have trailing slash.\"\n        },\n        \"default_redirect_url\": {\n          \"type\": \"string\",\n          \"description\": \"`default_redirect_url` is the URL the backend redirects to after it successfully verified\\nthe response from any third party provider.\\n\\nMust not have trailing slash.\"\n        },\n        \"allowed_redirect_urls\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"description\": \"`allowed_redirect_urls` is a list of URLs the backend is allowed to redirect to after third party sign-in was\\nsuccessful.\\n\\nSupports wildcard matching through globbing. e.g. `https://*.example.com` will allow `https://foo.example.com`\\nand `https://bar.example.com` to be accepted.\\n\\nGlobbing is also supported for paths, e.g. `https://foo.example.com/*` will match `https://foo.example.com/page1`\\nand `https://foo.example.com/page2`.\\n\\nA double asterisk (`**`) acts as a \\\"super\\\"-wildcard/match-all.\\n\\nSee [here](https://pkg.go.dev/github.com/gobwas/glob#Compile) for more on globbing.\\n\\nMust not be empty if any of the [`providers`](#providers) are `enabled`. URLs in the list must not have a trailing slash.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"ThirdPartyProvider\": {\n      \"if\": {\n        \"properties\": {\n          \"enabled\": {\n            \"const\": true\n          }\n        }\n      },\n      \"then\": {\n        \"required\": [\n          \"client_id\",\n          \"secret\"\n        ]\n      },\n      \"else\": {},\n      \"properties\": {\n        \"allow_linking\": {\n          \"type\": \"boolean\",\n          \"description\": \"`allow_linking` indicates whether existing accounts can be automatically linked with this provider.\\n\\nLinking is based on matching one of the email addresses of an existing user account with the (primary)\\nemail address of the third party provider account.\"\n        },\n        \"client_id\": {\n          \"type\": \"string\",\n          \"description\": \"`client_id` is the ID of the OAuth/OIDC client. Must be obtained from the provider.\\n\\nRequired if the provider is `enabled`.\"\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether this provider is enabled.\",\n          \"default\": false\n        },\n        \"prompt\": {\n          \"type\": \"string\",\n          \"description\": \"`prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent.\\nPossible values are:\\n- login\\n- none\\n- consent\\n- select_account\\nPlease note that not all providers support all values. Check the corresponding docs of the provider for supported values.\"\n        },\n        \"secret\": {\n          \"type\": \"string\",\n          \"description\": \"`secret` is the client secret for the OAuth/OIDC client. Must be obtained from the provider.\\n\\nRequired if the provider is `enabled`.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"title\": \"provider\"\n    },\n    \"ThirdPartyProviders\": {\n      \"properties\": {\n        \"apple\": {\n          \"$ref\": \"#/$defs/ThirdPartyProvider\",\n          \"description\": \"`apple` contains the provider configuration for Apple.\"\n        },\n        \"discord\": {\n          \"$ref\": \"#/$defs/ThirdPartyProvider\",\n          \"description\": \"`discord` contains the provider configuration for Discord.\"\n        },\n        \"github\": {\n          \"$ref\": \"#/$defs/ThirdPartyProvider\",\n          \"description\": \"`github` contains the provider configuration for GitHub.\"\n        },\n        \"google\": {\n          \"$ref\": \"#/$defs/ThirdPartyProvider\",\n          \"description\": \"`google` contains the provider configuration for Google.\"\n        },\n        \"linkedin\": {\n          \"$ref\": \"#/$defs/ThirdPartyProvider\",\n          \"description\": \"`linkedin` contains the provider configuration for LinkedIn.\"\n        },\n        \"microsoft\": {\n          \"$ref\": \"#/$defs/ThirdPartyProvider\",\n          \"description\": \"`microsoft` contains the provider configuration for Microsoft.\"\n        },\n        \"facebook\": {\n          \"$ref\": \"#/$defs/ThirdPartyProvider\",\n          \"description\": \"`facebook` contains the provider configuration for Facebook.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Username\": {\n      \"properties\": {\n        \"acquire_on_login\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_on_login` determines whether users, provided that they do not already have set a username,\\n\\tare prompted to provide a username on login.\",\n          \"default\": true\n        },\n        \"acquire_on_registration\": {\n          \"type\": \"boolean\",\n          \"description\": \"`acquire_on_registration` determines whether users are prompted to provide a username on registration.\",\n          \"default\": true\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` determines whether users can set a unique username.\\n\\nUsernames can contain letters (a-z,A-Z), numbers (0-9), and underscores.\",\n          \"default\": false\n        },\n        \"max_length\": {\n          \"type\": \"integer\",\n          \"description\": \"`max_length` specifies the maximum allowed length of a username.\",\n          \"default\": 32\n        },\n        \"min_length\": {\n          \"type\": \"integer\",\n          \"description\": \"`min_length` specifies the minimum length of a username.\",\n          \"default\": 3\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"`optional` determines whether users must provide a username when prompted. The username can only be changed but\\nnot deleted if usernames are required (`optional: false`).\",\n          \"default\": true\n        },\n        \"use_as_login_identifier\": {\n          \"type\": \"boolean\",\n          \"description\": \"`use_as_login_identifier` determines whether usernames, if enabled, can be used for logging in.\",\n          \"default\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"WebauthnSettings\": {\n      \"properties\": {\n        \"relying_party\": {\n          \"$ref\": \"#/$defs/RelyingParty\",\n          \"title\": \"relying_party\"\n        },\n        \"timeout\": {\n          \"type\": \"integer\",\n          \"description\": \"Deprecated, use `timeouts` instead.\",\n          \"default\": 60000\n        },\n        \"timeouts\": {\n          \"$ref\": \"#/$defs/WebauthnTimeouts\",\n          \"title\": \"timeouts\",\n          \"description\": \"`timeouts` specifies the timeouts for passkey/WebAuthn registration and login.\"\n        },\n        \"user_verification\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"required\",\n            \"preferred\",\n            \"discouraged\"\n          ],\n          \"description\": \"Deprecated, use `passkey.user_verification` instead\",\n          \"default\": \"preferred\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"description\": \"WebauthnSettings defines the settings for the webauthn authentication mechanism\"\n    },\n    \"WebauthnTimeouts\": {\n      \"properties\": {\n        \"registration\": {\n          \"type\": \"integer\",\n          \"description\": \"`registration` determines the time, in milliseconds, that the client is willing to wait for the credential\\ncreation request to the WebAuthn API to complete.\",\n          \"default\": 600000\n        },\n        \"login\": {\n          \"type\": \"integer\",\n          \"description\": \"`login` determines the time, in milliseconds, that the client is willing to wait for the credential\\n request to the WebAuthn API to complete.\",\n          \"default\": 600000\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Webhook\": {\n      \"properties\": {\n        \"callback\": {\n          \"type\": \"string\",\n          \"description\": \"`callback` specifies the URL to which the change data will be sent.\"\n        },\n        \"events\": {\n          \"$ref\": \"#/$defs/Events\",\n          \"items\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"user\",\n              \"user.create\",\n              \"user.delete\",\n              \"user.login\",\n              \"user.update\",\n              \"user.update.email\",\n              \"user.update.email.create\",\n              \"user.update.email.delete\",\n              \"user.update.email.primary\",\n              \"user.update.password.update\",\n              \"user.update.username\",\n              \"user.update.username.create\",\n              \"user.update.username.delete\",\n              \"user.update.username.update\",\n              \"email.send\"\n            ],\n            \"title\": \"events\",\n            \"meta:enum\": {\n              \"email.send\": \"Triggers on: an email was sent or should be sent\",\n              \"user\": \"Triggers on: user creation, user deletion, user update, email creation, email deletion, change of primary email\",\n              \"user.create\": \"Triggers on: user creation\",\n              \"user.delete\": \"Triggers on: user deletion\",\n              \"user.login\": \"Triggers on: user login\",\n              \"user.update\": \"Triggers on: user update, email creation, email deletion, change of primary email\",\n              \"user.update.email\": \"Triggers on: email creation, email deletion, change of primary email\",\n              \"user.update.email.create\": \"Triggers on: email creation\",\n              \"user.update.email.delete\": \"Triggers on: email deletion\",\n              \"user.update.email.primary\": \"Triggers on: change of primary email\",\n              \"user.update.password.update\": \"Triggers on: change of password\",\n              \"user.update.username\": \"Triggers on: username creation, username deletion, change of username\",\n              \"user.update.username.create\": \"Triggers on: username creation\",\n              \"user.update.username.delete\": \"Triggers on: username deletion\",\n              \"user.update.username.update\": \"Triggers on: change of username\"\n            }\n          },\n          \"title\": \"events\",\n          \"description\": \"`events` is a list of events this hook listens for.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"title\": \"hooks\"\n    },\n    \"WebhookSettings\": {\n      \"properties\": {\n        \"allow_time_expiration\": {\n          \"type\": \"boolean\",\n          \"description\": \"`allow_time_expiration` determines whether webhooks are disabled when unused for 30 days\\n(only for database webhooks).\",\n          \"default\": false\n        },\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"`enabled` enables the webhook feature.\",\n          \"default\": false\n        },\n        \"hooks\": {\n          \"$ref\": \"#/$defs/Webhooks\",\n          \"title\": \"hooks\",\n          \"description\": \"`hooks` is a list of Webhook configurations.\\n\\nWhen using environment variables the value for the `WEBHOOKS_HOOKS` key must be specified in the following\\nformat:\\n`{\\\"callback\\\":\\\"http://app.com/usercb\\\",\\\"events\\\":[\\\"user\\\"]};{\\\"callback\\\":\\\"http://app.com/emailcb\\\",\\\"events\\\":[\\\"email.send\\\"]}`\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\"\n    },\n    \"Webhooks\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/Webhook\"\n      },\n      \"type\": \"array\"\n    }\n  },\n  \"title\": \"Config\"\n}"
  },
  {
    "path": "backend/json_schema/hanko.user_import.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://github.com/teamhanko/hanko/backend/cmd/user/import-or-export-list\",\n  \"$ref\": \"#/$defs/ImportOrExportList\",\n  \"$defs\": {\n    \"Emails\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/ImportOrExportEmail\"\n      },\n      \"type\": \"array\",\n      \"description\": \"Emails Array of email addresses\"\n    },\n    \"ImportOTPSecret\": {\n      \"properties\": {\n        \"secret\": {\n          \"type\": \"string\",\n          \"description\": \"Secret of the TOTP credential. TOTP credential must be generated for a period of 30 seconds and SHA1 hash algorithm.\"\n        },\n        \"created_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"CreatedAt optional timestamp when the otp secret was created. Will be set to the import date if not provided.\"\n        },\n        \"updated_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"UpdatedAt optional timestamp of the last update to the otp secret. Will be set to the import date if not provided.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"secret\"\n      ]\n    },\n    \"ImportOrExportEmail\": {\n      \"properties\": {\n        \"address\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"description\": \"Address Valid email address\"\n        },\n        \"is_primary\": {\n          \"type\": \"boolean\",\n          \"description\": \"IsPrimary indicates if this is the primary email of the users. In the Emails array there has to be exactly one primary email.\"\n        },\n        \"is_verified\": {\n          \"type\": \"boolean\",\n          \"description\": \"IsVerified indicates if the email address was previously verified.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"address\",\n        \"is_primary\",\n        \"is_verified\"\n      ],\n      \"title\": \"ImportEmail\",\n      \"description\": \"ImportOrExportEmail The import/export format for a user's email\"\n    },\n    \"ImportOrExportEntry\": {\n      \"properties\": {\n        \"user_id\": {\n          \"type\": \"string\",\n          \"description\": \"UserID optional uuid.v4. If not provided a new one will be generated for the user\"\n        },\n        \"emails\": {\n          \"$ref\": \"#/$defs/Emails\",\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"description\": \"Emails optional list of emails\"\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"description\": \"Username optional username of the user\"\n        },\n        \"webauthn_credentials\": {\n          \"$ref\": \"#/$defs/ImportWebauthnCredentials\",\n          \"description\": \"WebauthnCredentials optional list of WebAuthn credentials of a user. Includes passkeys and MFA credentials.\"\n        },\n        \"password\": {\n          \"$ref\": \"#/$defs/ImportPasswordCredential\",\n          \"description\": \"Password optional password.\"\n        },\n        \"otp_secret\": {\n          \"$ref\": \"#/$defs/ImportOTPSecret\",\n          \"description\": \"OTPSecret optional TOTP secret for MFA.\"\n        },\n        \"created_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"CreatedAt optional timestamp of the users' creation. Will be set to the import date if not provided.\"\n        },\n        \"updated_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"UpdatedAt optional timestamp of the last update to the user. Will be set to the import date if not provided.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"emails\",\n        \"password\",\n        \"otp_secret\"\n      ],\n      \"title\": \"ImportEntry\",\n      \"description\": \"ImportOrExportEntry represents a user to be imported/export to the Hanko database\"\n    },\n    \"ImportOrExportList\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/ImportOrExportEntry\"\n      },\n      \"type\": \"array\",\n      \"description\": \"ImportOrExportList a list of ImportEntries\",\n      \"examples\": [\n        [\n          {\n            \"user_id\": \"a9ae6bc8-d829-43de-b672-f50230833877\",\n            \"emails\": [\n              {\n                \"address\": \"test@example.com\",\n                \"is_primary\": true,\n                \"is_verified\": true\n              },\n              {\n                \"address\": \"test+1@example.com\",\n                \"is_primary\": false,\n                \"is_verified\": false\n              }\n            ],\n            \"password\": null,\n            \"otp_secret\": null,\n            \"created_at\": \"2024-08-17T12:05:15.651387237Z\",\n            \"updated_at\": \"2024-08-17T12:05:15.651387237Z\"\n          },\n          {\n            \"user_id\": \"2f0649cf-c71e-48a5-92c3-210addb80281\",\n            \"emails\": [\n              {\n                \"address\": \"test2@example.com\",\n                \"is_primary\": true,\n                \"is_verified\": true\n              },\n              {\n                \"address\": \"test2+1@example.com\",\n                \"is_primary\": false,\n                \"is_verified\": false\n              }\n            ],\n            \"password\": null,\n            \"otp_secret\": null,\n            \"created_at\": \"2024-08-17T12:05:15.651387237Z\",\n            \"updated_at\": \"2024-08-17T12:05:15.651387237Z\"\n          }\n        ],\n        [\n          {\n            \"emails\": null,\n            \"username\": \"example\",\n            \"password\": {\n              \"password\": \"$2a$12$mFbud0mLsD/q.WG7/9pNQemlAHs3H4o8zAv44gsUF1v1awsdqTh7.\",\n              \"created_at\": \"2024-08-17T12:05:15.651387237Z\",\n              \"updated_at\": \"2024-08-17T12:05:15.651387237Z\"\n            },\n            \"otp_secret\": null\n          }\n        ]\n      ]\n    },\n    \"ImportPasswordCredential\": {\n      \"properties\": {\n        \"password\": {\n          \"type\": \"string\",\n          \"description\": \"Password hash of the password in bcrypt format.\"\n        },\n        \"created_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"CreatedAt optional timestamp when the password was created. Will be set to the import date if not provided.\"\n        },\n        \"updated_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"UpdatedAt optional timestamp of the last update to the password. Will be set to the import date if not provided.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"password\"\n      ]\n    },\n    \"ImportWebauthnCredential\": {\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"description\": \"ID of the WebAuthn credential.\"\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"description\": \"Optional Name of the WebAuthn credential.\"\n        },\n        \"public_key\": {\n          \"type\": \"string\",\n          \"description\": \"The PublicKey of the credential.\"\n        },\n        \"attestation_type\": {\n          \"type\": \"string\",\n          \"description\": \"The AttestationType the credential was created with.\"\n        },\n        \"aaguid\": {\n          \"$ref\": \"#/$defs/UUID\",\n          \"description\": \"Optional AAGUID of the authenticator on which the credential was created on.\"\n        },\n        \"sign_count\": {\n          \"type\": \"integer\",\n          \"description\": \"Optional SignCount of the WebAuthn credential.\"\n        },\n        \"last_used_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"LastUsedAt optional timestamp when the WebAuthn credential was last used.\"\n        },\n        \"created_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"CreatedAt optional timestamp of the WebAuthn credentials' creation. Will be set to the import date if not provided.\"\n        },\n        \"updated_at\": {\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"description\": \"UpdatedAt optional timestamp of the last update to the WebAuthn credential. Will be set to the import date if not provided.\"\n        },\n        \"transports\": {\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"type\": \"array\",\n          \"description\": \"Optional list of supported Transports by the authenticator.\"\n        },\n        \"backup_eligible\": {\n          \"type\": \"boolean\",\n          \"description\": \"BackupEligible flag indicates if the WebAuthn credential can be backed up (e.g. in Apple KeyChain, ...). If the information is not available set it to false.\"\n        },\n        \"backup_state\": {\n          \"type\": \"boolean\",\n          \"description\": \"BackupState flag indicates if the WebAuthn credential is backed up (e.g. in Apple KeyChain, ...). If the information is not available set it to false.\"\n        },\n        \"mfa_only\": {\n          \"type\": \"boolean\",\n          \"description\": \"MFAOnly flag indicates if the WebAuthn credential can only be used in combination with another login factor (e.g. password, ...).\"\n        },\n        \"user_handle\": {\n          \"type\": \"string\",\n          \"description\": \"UserHandle optional user id which was used to create the credential with.\\nPopulate only when user id was not an uuid v4 and the WebAuthn credential is not an MFAOnly credential.\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"required\": [\n        \"id\",\n        \"name\",\n        \"public_key\",\n        \"attestation_type\",\n        \"aaguid\",\n        \"sign_count\",\n        \"last_used_at\",\n        \"created_at\",\n        \"updated_at\",\n        \"transports\",\n        \"backup_eligible\",\n        \"backup_state\",\n        \"mfa_only\",\n        \"user_handle\"\n      ]\n    },\n    \"ImportWebauthnCredentials\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/ImportWebauthnCredential\"\n      },\n      \"type\": \"array\"\n    },\n    \"UUID\": {\n      \"items\": {\n        \"type\": \"integer\"\n      },\n      \"type\": \"array\",\n      \"maxItems\": 16,\n      \"minItems\": 16\n    }\n  },\n  \"title\": \"User import\"\n}"
  },
  {
    "path": "backend/mail/locales/passcode.bn.yaml",
    "content": "login_text:\n  description: \"The sign in content of the text email.\"\n  other: \"আপনার পরিচয় যাচাই করতে নিচের পাসকোডটি দিন:\"\nttl_text:\n  description: \"The length how long the passcode is valid.\"\n  other: \"পাসকোডটি {{ .TTL }} মিনিটের জন্য বৈধ।\"\nemail_subject_login:\n  description: \"\"\n  other: \"{{ .Code }} হল আপনার পাসকোড {{ .ServiceName }} এর জন্য\"\nsubject_email_verification:\n  description: \"\"\n  other: \"আপনার ইমেল ঠিকানা যাচাই করতে পাসকোড {{ .Code }} ব্যবহার করুন\"\nsubject_login:\n  description: \"\"\n  other: \"আপনার অ্যাকাউন্টে লগইন করতে পাসকোড {{ .Code }} ব্যবহার করুন\"\nsubject_recovery:\n  description: \"\"\n  other: \"আপনার অ্যাকাউন্ট পুনরুদ্ধার করতে পাসকোড {{ .Code }} ব্যবহার করুন\"\nemail_verification_text:\n  description: \"\"\n  other: \"আপনার ইমেল ঠিকানা যাচাই করতে নিচের পাসকোডটি দিন:\"\nrecovery_text:\n  description: \"The content of the recovery text email.\"\n  other: \"লগইন স্ক্রীনে নিচের পাসকোডটি দিন:\"\n\nsubject_email_login_attempted:\n  description: \"Subject for notification about a login attempt.\"\n  other: \"প্রদত্ত ইমেল ঠিকানা চিহ্নিত হয়নি\"\nemail_login_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to log in to a specific service using an unrecognized email address.\"\n  other: \"আপনি বা অন্য কেউ {{ .ServiceName }} তে সাইন ইন করার চেষ্টা করেছেন, তবে প্রদত্ত ইমেল ঠিকানা চিহ্নিত হয়নি। দয়া করে আগে একটি অ্যাকাউন্ট তৈরি করুন।\"\n\nsubject_email_registration_attempted:\n  description: \"Subject for notification about a registration attempt.\"\n  other: \"প্রদত্ত ইমেল ঠিকানা ইতিমধ্যেই ব্যবহৃত\"\nemail_registration_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to register for a specific service using an email address that is already in use.\"\n  other: \"আপনি বা অন্য কেউ {{ .ServiceName }} এর জন্য একটি ইমেল নিবন্ধন করার চেষ্টা করেছেন, কিন্তু প্রদত্ত ইমেল ঠিকানা ইতিমধ্যেই নিবন্ধিত। দয়া করে তার পরিবর্তে লগইন করার চেষ্টা করুন।\"\n"
  },
  {
    "path": "backend/mail/locales/passcode.de.yaml",
    "content": "login_text:\n  description: \"The sign in content of the text email.\"\n  other: \"Geben Sie den folgenden Passcode ein, um Ihre Identität zu überprüfen:\"\nttl_text:\n  description: \"The length how long the passcode is valid.\"\n  other: \"Der Passcode ist {{ .TTL }} Minuten lang gültig.\"\nemail_subject_login:\n  description: \"\"\n  other: \"{{ .Code }} ist Ihr Passcode für {{ .ServiceName }}\"\nsubject_email_verification:\n  description: \"\"\n  other: \"Verwenden Sie den Passcode {{ .Code }}, um Ihre E-Mail-Adresse zu bestätigen\"\nsubject_login:\n  description: \"\"\n  other: \"Verwenden Sie den Passcode {{ .Code }}, um sich in Ihr Konto einzuloggen\"\nsubject_recovery:\n  description: \"\"\n  other: \"Verwenden Sie den Passcode {{ .Code }}, um Ihr Konto wiederherzustellen\"\nemail_verification_text:\n  description: \"\"\n  other: \"Geben Sie den folgenden Passcode ein, um Ihre E-Mail-Adresse zu bestätigen:\"\nrecovery_text:\n  description: \"The content of the recovery text email.\"\n  other: \"Geben Sie auf Ihrem Anmeldebildschirm den folgenden Passcode ein:\"\n\nsubject_email_login_attempted:\n  description: \"Subject for notification about a login attempt.\"\n  other: \"Die angegebene E-Mail-Adresse wird nicht erkannt.\"\nemail_login_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to log in to a specific service using an unrecognized email address.\"\n  other: \"Sie oder eine andere Person haben versucht, sich bei {{ .ServiceName }} anzumelden, aber die angegebene E-Mail-Adresse wird nicht erkannt. Bitte erstellen Sie zunächst ein Konto.\"\n\nsubject_email_registration_attempted:\n  description: \"Subject for notification about a registration attempt.\"\n  other: \"Die angegebene E-Mail-Adresse ist bereits vergeben.\"\nemail_registration_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to register for a specific service using an email address that is already in use.\"\n  other: \"Sie oder eine andere Person haben versucht, eine E-Mail für {{ .ServiceName }} zu registrieren, aber die angegebene E-Mail-Adresse ist bereits registriert. Bitte versuchen Sie stattdessen, sich anzumelden.\"\n\n"
  },
  {
    "path": "backend/mail/locales/passcode.en.yaml",
    "content": "login_text:\n  description: \"The sign in content of the text email.\"\n  other: \"Enter the following passcode to verify your identity:\"\nttl_text:\n  description: \"The length how long the passcode is valid.\"\n  other: \"The passcode is valid for {{ .TTL }} minutes.\"\nemail_subject_login:\n  description: \"\"\n  other: \"{{ .Code }} is your passcode for {{ .ServiceName }}\"\nsubject_email_verification:\n  description: \"\"\n  other: \"Use passcode {{ .Code }} to verify your email address\"\nsubject_login:\n  description: \"\"\n  other: \"Use passcode {{ .Code }} to login to your account\"\nsubject_recovery:\n  description: \"\"\n  other: \"Use passcode {{ .Code }} to recover your account\"\nemail_verification_text:\n  description: \"\"\n  other: \"Enter the following passcode to verify your email address:\"\nrecovery_text:\n  description: \"The content of the recovery text email.\"\n  other: \"Enter the following passcode on your login screen:\"\n\nsubject_email_login_attempted:\n  description: \"Subject for notification about a login attempt.\"\n  other: \"Provided email address is not recognized\"\nemail_login_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to log in to a specific service using an unrecognized email address.\"\n  other: \"You or someone else tried to sign in to {{ .ServiceName }}, but the provided email address is not recognized. Please create an account first.\"\n\nsubject_email_registration_attempted:\n  description: \"Subject for notification about a registration attempt.\"\n  other: \"Provided email address already taken\"\nemail_registration_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to register for a specific service using an email address that is already in use.\"\n  other: \"You or someone else tried to register an email for {{ .ServiceName }}, but the provided email address is already registered. Please try to log in instead.\"\n\n"
  },
  {
    "path": "backend/mail/locales/passcode.fr.yaml",
    "content": "login_text:\n  description: \"The sign in content of the text email.\"\n  other: \"Entrez le code d'accès suivant pour vérifier votre identité :\"\nttl_text:\n  description: \"The length how long the passcode is valid.\"\n  other: \"Le code d'accès est valable pendant {{ .TTL }} minutes.\"\nemail_subject_login:\n  description: \"\"\n  other: \"{{ .Code }} est votre code d'accès pour {{ .ServiceName }}\"\nsubject_email_verification:\n  description: \"\"\n  other: \"Utilisez le code d'accès {{ .Code }} pour vérifier votre adresse e-mail\"\nsubject_login:\n  description: \"\"\n  other: \"Utilisez le code d'accès {{ .Code }} pour vous connecter à votre compte\"\nsubject_recovery:\n  description: \"\"\n  other: \"Utilisez le code d'accès {{ .Code }} pour récupérer votre compte\"\nemail_verification_text:\n  description: \"\"\n  other: \"Entrez le code d'accès suivant pour vérifier votre adresse e-mail :\"\nrecovery_text:\n  description: \"The content of the recovery text email.\"\n  other: \"Entrez le code d'accès suivant sur votre écran de connexion :\"\nsubject_email_login_attempted:\n  description: \"Subject for notification about a login attempt.\"\n  other: \"L'adresse e-mail fournie n'est pas reconnue\"\nemail_login_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to log in to a specific service using an unrecognized email address.\"\n  other: \"Vous ou quelqu'un d'autre avez essayé de vous connecter à {{ .ServiceName }}, mais l'adresse e-mail fournie n'a pas été reconnue. Veuillez d'abord créer un compte.\"\nsubject_email_registration_attempted:\n  description: \"Subject for notification about a registration attempt.\"\n  other: \"L'adresse e-mail fournie est déjà utilisée\"\nemail_registration_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to register for a specific service using an email address that is already in use.\"\n  other: \"Vous ou quelqu'un d'autre avez tenté d'enregistrer un e-mail pour {{ .ServiceName }}, mais l'adresse e-mail fournie est déjà enregistrée. Veuillez essayer de vous connecter à la place.\"\n"
  },
  {
    "path": "backend/mail/locales/passcode.it.yaml",
    "content": "login_text:\n  description: \"The sign in content of the text email.\"\n  other: \"Inserisci il seguente codice di accesso per verificare la tua identità:\"\nttl_text:\n  description: \"The length how long the passcode is valid.\"\n  other: \"Il codice di accesso è valido per {{ .TTL }} minuti.\"\nemail_subject_login:\n  description: \"\"\n  other: \"{{ .Code }} è il tuo codice di accesso per {{ .ServiceName }}\"\nsubject_email_verification:\n  description: \"\"\n  other: \"Usa il codice di accesso {{ .Code }} per verificare il tuo indirizzo e-mail\"\nsubject_login:\n  description: \"\"\n  other: \"Usa il codice di accesso {{ .Code }} per accedere al tuo account\"\nsubject_recovery:\n  description: \"\"\n  other: \"Usa il codice di accesso {{ .Code }} per recuperare il tuo account\"\nemail_verification_text:\n  description: \"\"\n  other: \"Inserisci il seguente codice di accesso per verificare il tuo indirizzo e-mail:\"\nrecovery_text:\n  description: \"The content of the recovery text email.\"\n  other: \"Inserisci il seguente codice di accesso nella tua schermata di login:\"\nsubject_email_login_attempted:\n  description: \"Subject for notification about a login attempt.\"\n  other: \"Indirizzo e-mail fornito non riconosciuto\"\nemail_login_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to log in to a specific service using an unrecognized email address.\"\n  other: \"Tu o qualcun altro avete provato ad accedere a {{ .ServiceName }}, ma l'indirizzo e-mail fornito non è stato riconosciuto. Per favore, crea prima un account.\"\nsubject_email_registration_attempted:\n  description: \"Subject for notification about a registration attempt.\"\n  other: \"Indirizzo e-mail fornito già in uso\"\nemail_registration_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to register for a specific service using an email address that is already in use.\"\n  other: \"Tu o qualcun altro avete provato a registrare un'e-mail per {{ .ServiceName }}, ma l'indirizzo e-mail fornito è già registrato. Per favore, prova a fare il login invece.\"\n"
  },
  {
    "path": "backend/mail/locales/passcode.nl.yaml",
    "content": "login_text:\n  description: \"The sign in content of the text email.\"\n  other: \"Voer de volgende code in om je identiteit te verifiëren:\"\nttl_text:\n  description: \"The length how long the passcode is valid.\"\n  other: \"De code is {{ .TTL }} minuten geldig.\"\nemail_subject_login:\n  description: \"\"\n  other: \"{{ .Code }} is je verificatiecode voor {{ .ServiceName }}\"\nsubject_email_verification:\n  description: \"\"\n  other: \"Gebruik code {{ .Code }} om je e-mailadres te verifiëren\"\nsubject_login:\n  description: \"\"\n  other: \"Gebruik code {{ .Code }} om in te loggen op je account\"\nsubject_recovery:\n  description: \"\"\n  other: \"Gebruik code {{ .Code }} om je account te herstellen\"\nemail_verification_text:\n  description: \"\"\n  other: \"Voer de volgende code in om je e-mailadres te verifiëren:\"\nrecovery_text:\n  description: \"The content of the recovery text email.\"\n  other: \"Voer de volgende code in op het inlogscherm:\"\n\nsubject_email_login_attempted:\n  description: \"Subject for notification about a login attempt.\"\n  other: \"Het opgegeven e-mailadres wordt niet herkend\"\nemail_login_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to log in to a specific service using an unrecognized email address.\"\n  other: \"Jij of iemand anders heeft geprobeerd in te loggen bij {{ .ServiceName }}, maar het opgegeven e-mailadres is niet bekend. Maak eerst een account aan.\"\n\nsubject_email_registration_attempted:\n  description: \"Subject for notification about a registration attempt.\"\n  other: \"Het opgegeven e-mailadres is al in gebruik\"\nemail_registration_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to register for a specific service using an email address that is already in use.\"\n  other: \"Jij of iemand anders heeft geprobeerd een e-mailadres te registreren voor {{ .ServiceName }}, maar dit e-mailadres is al geregistreerd. Probeer in plaats daarvan in te loggen.\"\n"
  },
  {
    "path": "backend/mail/locales/passcode.pt-BR.yaml",
    "content": "login_text:\n  description: \"The sign in content of the text email.\"\n  other: \"Digite o seguinte código de acesso para verificar sua identidade:\"\nttl_text:\n  description: \"The length how long the passcode is valid.\"\n  other: \"O código de acesso é válido por {{ .TTL }} minutos.\"\nemail_subject_login:\n  description: \"\"\n  other: \"{{ .Code }} é o seu código de acesso para {{ .ServiceName }}\"\nsubject_email_verification:\n  description: \"\"\n  other: \"Use o código de acesso {{ .Code }} para verificar o seu endereço de e-mail\"\nsubject_login:\n  description: \"\"\n  other: \"Use o código de acesso {{ .Code }} para fazer login na sua conta\"\nsubject_recovery:\n  description: \"\"\n  other: \"Use o código de acesso {{ .Code }} para recuperar sua conta\"\nemail_verification_text:\n  description: \"\"\n  other: \"Digite o seguinte código de acesso para verificar o seu endereço de e-mail:\"\nrecovery_text:\n  description: \"The content of the recovery text email.\"\n  other: \"Digite o seguinte código de acesso na tela de login:\"\n\nsubject_email_login_attempted:\n  description: \"Subject for notification about a login attempt.\"\n  other: \"Endereço de e-mail fornecido não reconhecido\"\nemail_login_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to log in to a specific service using an unrecognized email address.\"\n  other: \"Você ou outra pessoa tentou fazer login no {{ .ServiceName }}, mas o endereço de e-mail fornecido não foi reconhecido. Por favor, crie uma conta primeiro.\"\n\nsubject_email_registration_attempted:\n  description: \"Subject for notification about a registration attempt.\"\n  other: \"Endereço de e-mail fornecido já está em uso\"\nemail_registration_attempted_text:\n  description: \"Notifies the recipient that either they or someone else attempted to register for a specific service using an email address that is already in use.\"\n  other: \"Você ou outra pessoa tentou registrar um e-mail para {{ .ServiceName }}, mas o endereço de e-mail fornecido já está registrado. Por favor, tente fazer login em vez disso.\"\n"
  },
  {
    "path": "backend/mail/locales/passcode.zh-CN.yaml",
    "content": "login_text:\n  description: \"deng lu\"\n  other: \"请输入以下验证码以验证您的身份：\"\nttl_text:\n  description: \"验证码的有效时长。\"\n  other: \"验证码在 {{ .TTL }} 分钟内有效。\"\nemail_subject_login:\n  description: \"\"\n  other: \"您 {{ .ServiceName }} 的验证码为{{ .Code }} \"\nsubject_email_verification:\n  description: \"\"\n  other: \"使用验证码 {{ .Code }} 验证您的电子邮件地址\"\nsubject_login:\n  description: \"\"\n  other: \"使用验证码 {{ .Code }} 登录您的账户\"\nsubject_recovery:\n  description: \"\"\n  other: \"使用验证码 {{ .Code }} 恢复您的账户\"\nemail_verification_text:\n  description: \"\"\n  other: \"请输入以下验证码以验证您的电子邮件地址：\"\nrecovery_text:\n  description: \"恢复邮件的内容。\"\n  other: \"请在登录页面输入以下验证码：\"\n\nsubject_email_login_attempted:\n  description: \"有关尝试登录的通知。\"\n  other: \"提供的电子邮件地址未被识别\"\nemail_login_attempted_text:\n  description: \"通知收件人，他们或其他人试图使用未被识别的电子邮件地址登录到特定服务。\"\n  other: \"您或其他人试图登录 {{ .ServiceName }}，但提供的电子邮件地址未被识别。请先创建一个账户。\"\n\nsubject_email_registration_attempted:\n  description: \"有关尝试注册的通知。\"\n  other: \"提供的电子邮件地址已被占用\"\nemail_registration_attempted_text:\n  description: \"通知收件人，他们或其他人试图使用已注册的电子邮件地址为特定服务注册。\"\n  other: \"您或其他人试图为 {{ .ServiceName }} 注册电子邮件，但提供的电子邮件地址已被注册。请尝试登录。\"\n"
  },
  {
    "path": "backend/mail/locales/security-notifications.bn.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"আপনার {{ .ServiceName }} অ্যাকাউন্টের পাসওয়ার্ড আপডেট করা হয়েছে।\"\nsubject_password_update:\n  description: \"\"\n  other: \"আপনার পাসওয়ার্ড আপডেট করা হয়েছে।\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"আপনার {{ .ServiceName }} অ্যাকাউন্টের প্রাথমিক ইমেল ঠিকানা {{ .OldEmailAddress }} থেকে {{ .NewEmailAddress }} এ আপডেট করা হয়েছে।\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"আপনার প্রাথমিক ইমেল ঠিকানা আপডেট করা হয়েছে।\"\n\nemail_create_text:\n  description: \"\"\n  other: \"আপনার {{ .ServiceName }} অ্যাকাউন্টে {{ .NewEmailAddress }} ইমেল ঠিকানা যোগ করা হয়েছে।\"\nsubject_email_create:\n  description: \"\"\n  other: \"একটি নতুন ইমেল ঠিকানা যোগ করা হয়েছে।\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"আপনার {{ .ServiceName }} অ্যাকাউন্ট থেকে {{ .DeletedEmailAddress }} ইমেল ঠিকানা মুছে ফেলা হয়েছে।\"\nsubject_email_delete:\n  description: \"\"\n  other: \"একটি ইমেল ঠিকানা মুছে ফেলা হয়েছে।\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"আপনার {{ .ServiceName }} অ্যাকাউন্টে একটি নতুন পাসকি যোগ করা হয়েছে।\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"একটি নতুন পাসকি যোগ করা হয়েছে।\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"আপনার {{ .ServiceName }} অ্যাকাউন্টের জন্য একটি নতুন দুই-ফ্যাক্টর (MFA) পদ্ধতি সফলভাবে যোগ করা হয়েছে।\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"একটি নতুন MFA পদ্ধতি যোগ করা হয়েছে\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"আপনার {{ .ServiceName }} অ্যাকাউন্টের জন্য একটি বিদ্যমান দুই-ফ্যাক্টর (MFA) পদ্ধতি মুছে ফেলা হয়েছে।\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"একটি MFA পদ্ধতি মুছে ফেলা হয়েছে।\""
  },
  {
    "path": "backend/mail/locales/security-notifications.de.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"Das Passwort für Ihr {{ .ServiceName }}-Konto wurde aktualisiert.\"\nsubject_password_update:\n  description: \"\"\n  other: \"Ihr Passwort wurde aktualisiert.\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"Die primäre E-Mail-Adresse für Ihr {{ .ServiceName }}-Konto wurde von {{ .OldEmailAddress }} auf {{ .NewEmailAddress }} aktualisiert.\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"Ihre primäre E-Mail-Adresse wurde aktualisiert.\"\n\nemail_create_text:\n  description: \"\"\n  other: \"Die E-Mail-Adresse {{ .NewEmailAddress }} wurde zu Ihrem {{ .ServiceName }}-Konto hinzugefügt.\"\nsubject_email_create:\n  description: \"\"\n  other: \"Eine neue E-Mail-Adresse wurde hinzugefügt.\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"Die E-Mail-Adresse {{ .DeletedEmailAddress }} wurde von Ihrem {{ .ServiceName }}-Konto entfernt.\"\nsubject_email_delete:\n  description: \"\"\n  other: \"Eine E-Mail-Adresse wurde entfernt.\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"Ein neuer Passkey wurde zu Ihrem {{ .ServiceName }}-Konto hinzugefügt.\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"Ein neuer Passkey wurde hinzugefügt.\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"Eine neue Multi-Faktor-Authentifizierung (MFA) wurde erfolgreich zu Ihrem {{ .ServiceName }}-Konto hinzugefügt.\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"Ein neues Multi-Faktor-Authentifizierungssystem (MFA) wurde hinzugefügt.\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"Die bestehende Multi-Faktor-Authentifizierung (MFA) wurde von Ihrem {{ .ServiceName }}-Konto gelöscht.\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"MFA-Methode entfernt\""
  },
  {
    "path": "backend/mail/locales/security-notifications.en.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"The password for your {{ .ServiceName }} account has been updated.\"\nsubject_password_update:\n  description: \"\"\n  other: \"Your password has been updated\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"The primary email address for your {{ .ServiceName }} account has been updated from {{ .OldEmailAddress }} to {{ .NewEmailAddress }}.\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"Your primary email address has been updated\"\n\nemail_create_text:\n  description: \"\"\n  other: \"The email address {{ .NewEmailAddress }} has been added to your {{ .ServiceName }} account.\"\nsubject_email_create:\n  description: \"\"\n  other: \"A new email address has been added\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"The email address {{ .DeletedEmailAddress }} has been removed from your {{ .ServiceName }} account.\"\nsubject_email_delete:\n  description: \"\"\n  other: \"An email address has been removed\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"A new passkey has been added to your {{ .ServiceName }} account.\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"A new passkey has been added\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"A new multi-factor (MFA) method has been successfully added to your {{ .ServiceName }} account.\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"A new MFA method has been added\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"An existing multi-factor (MFA) method has been removed from your {{ .ServiceName }} account.\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"MFA method removed\""
  },
  {
    "path": "backend/mail/locales/security-notifications.fr.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"Le mot de passe de votre compte {{ .ServiceName }} a été mis à jour.\"\nsubject_password_update:\n  description: \"\"\n  other: \"Votre mot de passe a été mis à jour.\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"L'adresse e-mail principale de votre compte {{ .ServiceName }} a été mise à jour de {{ .OldEmailAddress }} à {{ .NewEmailAddress }}.\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"Votre adresse e-mail principale a été mise à jour.\"\n\nemail_create_text:\n  description: \"\"\n  other: \"L'adresse e-mail {{ .NewEmailAddress }} a été ajoutée à votre compte {{ .ServiceName }}.\"\nsubject_email_create:\n  description: \"\"\n  other: \"Une nouvelle adresse e-mail a été ajoutée.\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"L'adresse e-mail {{ .DeletedEmailAddress }} a été supprimée de votre compte {{ .ServiceName }}.\"\nsubject_email_delete:\n  description: \"\"\n  other: \"Une adresse e-mail a été supprimée.\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"Une nouvelle clé d'accès a été ajoutée à votre compte {{ .ServiceName }}.\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"Une nouvelle clé d'accès a été ajoutée.\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"Une nouvelle méthode d'authentification multifactorielle (MFA) a été ajoutée avec succès à votre compte {{ .ServiceName }}.\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"Une nouvelle méthode d'authentification multifacteur a été ajoutée\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"Une méthode d'authentification multifactorielle (MFA) existante a été supprimée de votre compte {{ .ServiceName }}.\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"Méthode MFA supprimée\""
  },
  {
    "path": "backend/mail/locales/security-notifications.it.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"La password per il tuo account {{ .ServiceName }} è stata aggiornata.\"\nsubject_password_update:\n  description: \"\"\n  other: \"La tua password è stata aggiornata.\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"L'indirizzo email principale per il tuo account {{ .ServiceName }} è stato aggiornato da {{ .OldEmailAddress }} a {{ .NewEmailAddress }}.\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"Il tuo indirizzo email principale è stato aggiornato.\"\n\nemail_create_text:\n  description: \"\"\n  other: \"L'indirizzo email {{ .NewEmailAddress }} è stato aggiunto al tuo account {{ .ServiceName }}.\"\nsubject_email_create:\n  description: \"\"\n  other: \"Un nuovo indirizzo email è stato aggiunto.\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"L'indirizzo email {{ .DeletedEmailAddress }} è stato rimosso dal tuo account {{ .ServiceName }}.\"\nsubject_email_delete:\n  description: \"\"\n  other: \"Un indirizzo email è stato rimosso.\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"Una nuova chiave di accesso è stata aggiunta al tuo account {{ .ServiceName }}.\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"Una nuova chiave di accesso è stata aggiunta.\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"Un nuovo metodo multifattoriale (MFA) è stato aggiunto correttamente al tuo account {{ .ServiceName }}.\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"È stato aggiunto un nuovo metodo MFA\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"Un metodo multifattoriale (MFA) esistente è stato rimosso dal tuo account {{ .ServiceName }}.\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"Metodo MFA rimosso\""
  },
  {
    "path": "backend/mail/locales/security-notifications.nl.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"Het wachtwoord voor je {{ .ServiceName }}-account is bijgewerkt.\"\nsubject_password_update:\n  description: \"\"\n  other: \"Je wachtwoord is bijgewerkt.\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"Het primaire e-mailadres voor je {{ .ServiceName }}-account is gewijzigd van {{ .OldEmailAddress }} naar {{ .NewEmailAddress }}.\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"Je primaire e-mailadres is bijgewerkt.\"\n\nemail_create_text:\n  description: \"\"\n  other: \"Het e-mailadres {{ .NewEmailAddress }} is toegevoegd aan je {{ .ServiceName }}-account.\"\nsubject_email_create:\n  description: \"\"\n  other: \"Een nieuw e-mailadres is toegevoegd.\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"Het e-mailadres {{ .DeletedEmailAddress }} is verwijderd van je {{ .ServiceName }}-account.\"\nsubject_email_delete:\n  description: \"\"\n  other: \"Een e-mailadres is verwijderd.\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"Er is een nieuwe passkey toegevoegd aan je {{ .ServiceName }}-account.\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"Een nieuwe passkey is toegevoegd.\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"Er is een nieuwe tweestapsverificatiemethode (MFA) toegevoegd aan je {{ .ServiceName }}-account.\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"Een nieuwe MFA-methode is toegevoegd\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"Een bestaande tweestapsverificatiemethode (MFA) is verwijderd van je {{ .ServiceName }}-account.\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"Een MFA-methode is verwijderd.\"\n"
  },
  {
    "path": "backend/mail/locales/security-notifications.pt-BR.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"A senha da sua conta {{ .ServiceName }} foi atualizada.\"\nsubject_password_update:\n  description: \"\"\n  other: \"Sua senha foi atualizada.\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"O endereço de e-mail principal da sua conta {{ .ServiceName }} foi atualizado de {{ .OldEmailAddress }} para {{ .NewEmailAddress }}.\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"O seu endereço de e-mail principal foi atualizado.\"\n\nemail_create_text:\n  description: \"\"\n  other: \"O endereço de e-mail {{ .NewEmailAddress }} foi adicionado à sua conta {{ .ServiceName }}.\"\nsubject_email_create:\n  description: \"\"\n  other: \"Um novo endereço de e-mail foi adicionado.\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"O endereço de e-mail {{ .DeletedEmailAddress }} foi removido da sua conta {{ .ServiceName }}.\"\nsubject_email_delete:\n  description: \"\"\n  other: \"Um endereço de e-mail foi removido.\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"Uma nova chave de acesso foi adicionada à sua conta {{ .ServiceName }}.\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"Uma nova chave de acesso foi adicionada.\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"Um novo método de autenticação multifator (MFA) foi adicionado com sucesso à sua conta {{ .ServiceName }}.\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"Um novo método de MFA foi adicionado.\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"Um método de autenticação multifator (MFA) existente foi removido da sua conta {{ .ServiceName }}.\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"Um método de MFA foi removido.\""
  },
  {
    "path": "backend/mail/locales/security-notifications.zh-CN.yaml",
    "content": "password_update_text:\n  description: \"\"\n  other: \"您的 {{ .ServiceName }} 帐户密码已更新。\"\nsubject_password_update:\n  description: \"\"\n  other: \"您的密码已更新。\"\n\nprimary_email_update_text:\n  description: \"\"\n  other: \"您的 {{ .ServiceName }} 帐户的主要电子邮件地址已从 {{ .OldEmailAddress }} 更新为 {{ .NewEmailAddress }}。\"\nsubject_primary_email_update:\n  description: \"\"\n  other: \"您的主要电子邮件地址已更新。\"\n\nemail_create_text:\n  description: \"\"\n  other: \"电子邮件地址 {{ .NewEmailAddress }} 已添加到您的 {{ .ServiceName }} 帐户。\"\nsubject_email_create:\n  description: \"\"\n  other: \"已添加新的电子邮件地址。\"\n\nemail_delete_text:\n  description: \"\"\n  other: \"电子邮件地址 {{ .DeletedEmailAddress }} 已从您的 {{ .ServiceName }} 帐户中删除。\"\nsubject_email_delete:\n  description: \"\"\n  other: \"已删除电子邮件地址。\"\n\npasskey_create_text:\n  description: \"\"\n  other: \"您的 {{ .ServiceName }} 帐户已添加新密码密钥。\"\nsubject_passkey_create:\n  description: \"\"\n  other: \"已添加新密码密钥。\"\n\nmfa_create_text:\n  description: \"\"\n  other: \"您的 {{ .ServiceName }} 帐户已成功添加新的多因素身份验证 (MFA) 方法。\"\nsubject_mfa_create:\n  description: \"\"\n  other: \"新增了一种多因素身份验证 (MFA) 方法。\"\n\nmfa_delete_text:\n  description: \"\"\n  other: \"您的 {{ .ServiceName }} 帐户中已移除现有的多因素身份验证 (MFA) 方法。\"\nsubject_mfa_delete:\n  description: \"\"\n  other: \"MFA方法已移除\""
  },
  {
    "path": "backend/mail/mailer.go",
    "content": "package mail\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"gopkg.in/gomail.v2\"\n\t\"strconv\"\n)\n\ntype Mailer interface {\n\tSend(message *gomail.Message) error\n}\n\ntype mailer struct {\n\tdialer *gomail.Dialer\n}\n\nfunc NewMailer(config config.SMTP) (Mailer, error) {\n\tport, err := strconv.Atoi(config.Port)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse SMTP port: %w\", err)\n\t}\n\td := gomail.NewDialer(config.Host, port, config.User, config.Password)\n\treturn &mailer{\n\t\tdialer: d,\n\t}, nil\n}\n\nfunc (m *mailer) Send(message *gomail.Message) error {\n\treturn m.dialer.DialAndSend(message)\n}\n"
  },
  {
    "path": "backend/mail/mailer_test.go",
    "content": "package mail\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"testing\"\n)\n\nfunc TestNewMailer(t *testing.T) {\n\ttests := []struct {\n\t\tName      string\n\t\tInput     config.SMTP\n\t\tWantError bool\n\t}{\n\t\t{\n\t\t\tName: \"create mailer successful\",\n\t\t\tInput: config.SMTP{\n\t\t\t\tHost:     \"mail.example.com\",\n\t\t\t\tPort:     \"123\",\n\t\t\t\tUser:     \"example\",\n\t\t\t\tPassword: \"example\",\n\t\t\t},\n\t\t\tWantError: false,\n\t\t},\n\t\t{\n\t\t\tName: \"create mailer with incompatible port\",\n\t\t\tInput: config.SMTP{\n\t\t\t\tHost:     \"mail.example.com\",\n\t\t\t\tPort:     \"abc\",\n\t\t\t\tUser:     \"example\",\n\t\t\t\tPassword: \"example\",\n\t\t\t},\n\t\t\tWantError: true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tmailer, err := NewMailer(test.Input)\n\n\t\t\tif test.WantError {\n\t\t\t\tassert.Error(t, err)\n\t\t\t\tassert.Empty(t, mailer)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.NotEmpty(t, mailer)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/mail/render.go",
    "content": "package mail\n\nimport (\n\t\"bytes\"\n\t\"embed\"\n\t\"fmt\"\n\t\"github.com/nicksnyder/go-i18n/v2/i18n\"\n\t\"golang.org/x/text/language\"\n\t\"gopkg.in/yaml.v3\"\n\t\"html/template\"\n\t\"strings\"\n)\n\n//go:embed templates/* locales/*\nvar mailFS embed.FS\n\ntype Renderer struct {\n\ttemplatePlain *template.Template\n\tbundle        *i18n.Bundle\n\tlocalizer     *i18n.Localizer\n}\n\n// NewRenderer creates an instance of Renderer, which renders the templates (located in mail/templates) with locales (located in mail/locales)\nfunc NewRenderer() (*Renderer, error) {\n\tr := &Renderer{}\n\tbundle := i18n.NewBundle(language.English)\n\tdir, err := mailFS.ReadDir(\"locales\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read locales directory: %w\", err)\n\t}\n\tbundle.RegisterUnmarshalFunc(\"yaml\", yaml.Unmarshal)\n\tfor _, entry := range dir {\n\t\t_, _ = bundle.LoadMessageFileFS(mailFS, fmt.Sprintf(\"locales/%s\", entry.Name()))\n\t}\n\tr.bundle = bundle\n\n\t// add the translate function to the template, so it can be used inside\n\tfuncMap := template.FuncMap{\"t\": r.translate}\n\tt := template.New(\"root\").Funcs(funcMap)\n\t_, err = t.ParseFS(mailFS, \"templates/*.txt.tmpl\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load templates: %w\", err)\n\t}\n\tr.templatePlain = t\n\n\treturn r, nil\n}\n\n// translate is a helper function to translate texts in a template\nfunc (r *Renderer) translate(messageID string, templateData map[string]interface{}) string {\n\tlocalizer := i18n.NewLocalizer(r.bundle, templateData[\"renderer_lang\"].(string))\n\treturn localizer.MustLocalize(&i18n.LocalizeConfig{\n\t\tMessageID:    messageID,\n\t\tTemplateData: templateData,\n\t})\n}\n\n// RenderPlain renders a template with the given data and lang.\n// The lang can be the contents of Accept-Language headers as defined in http://www.ietf.org/rfc/rfc2616.txt.\nfunc (r *Renderer) RenderPlain(templateName string, lang string, data map[string]interface{}) (string, error) {\n\tr.localizer = i18n.NewLocalizer(r.bundle, lang) // set the localizer, so the test will be translated to the given language\n\tdata[\"renderer_lang\"] = lang\n\ttemplateBuffer := &bytes.Buffer{}\n\terr := r.templatePlain.ExecuteTemplate(templateBuffer, fmt.Sprintf(\"%s.txt.tmpl\", templateName), data)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to fill plain text template with data: %w\", err)\n\t}\n\treturn strings.TrimSpace(templateBuffer.String()), nil\n}\n\n// RenderHTML renders an HTML template with the given data and lang.\n// The lang can be the contents of Accept-Language headers as defined in http://www.ietf.org/rfc/rfc2616.txt.\nfunc (r *Renderer) RenderHTML(templateName string, lang string, data map[string]interface{}) (string, error) {\n\tvar buffer bytes.Buffer\n\n\tr.localizer = i18n.NewLocalizer(r.bundle, lang)\n\tdata[\"renderer_lang\"] = lang\n\n\ttemplateHTML := template.New(\"root\").Funcs(template.FuncMap{\"t\": r.translate})\n\tpatterns := []string{\"templates/layout.html.tmpl\", fmt.Sprintf(\"templates/%s.html.tmpl\", templateName)}\n\t_, err := templateHTML.ParseFS(mailFS, patterns...)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to parse html template: %w\", err)\n\t}\n\n\terr = templateHTML.ExecuteTemplate(&buffer, \"layout\", data)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to execute html template: %w\", err)\n\t}\n\n\treturn strings.TrimSpace(buffer.String()), nil\n}\n\nfunc (r *Renderer) Translate(lang string, messageID string, data map[string]interface{}) string {\n\tloc := i18n.NewLocalizer(r.bundle, lang)\n\treturn loc.MustLocalize(&i18n.LocalizeConfig{\n\t\tMessageID:    messageID,\n\t\tTemplateData: data,\n\t})\n}\n"
  },
  {
    "path": "backend/mail/render_test.go",
    "content": "package mail\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestNewRenderer(t *testing.T) {\n\trenderer, err := NewRenderer()\n\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, renderer)\n}\n\nfunc TestRenderer_RenderPlain(t *testing.T) {\n\trenderer, err := NewRenderer()\n\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, renderer)\n\n\ttemplateData := map[string]interface{}{\n\t\t\"TTL\":  5,\n\t\t\"Code\": \"123456\",\n\t}\n\n\ttests := []struct {\n\t\tName     string\n\t\tTemplate string\n\t\tLang     string\n\t\tExpected string\n\t\tWantErr  bool\n\t}{\n\t\t{\n\t\t\tName:     \"Login text template\",\n\t\t\tTemplate: \"login\",\n\t\t\tLang:     \"en\",\n\t\t\tExpected: \"Enter the following passcode to verify your identity:\\n\\n123456\\n\\nThe passcode is valid for 5 minutes.\",\n\t\t\tWantErr:  false,\n\t\t},\n\t\t{\n\t\t\tName:     \"Not existing template\",\n\t\t\tTemplate: \"NotExistingTemplate\",\n\t\t\tLang:     \"en\",\n\t\t\tExpected: \"\",\n\t\t\tWantErr:  true,\n\t\t},\n\t\t{\n\t\t\tName:     \"Login text template with unknown language\",\n\t\t\tTemplate: \"login\",\n\t\t\tLang:     \"xxx\",\n\t\t\tExpected: \"Enter the following passcode to verify your identity:\\n\\n123456\\n\\nThe passcode is valid for 5 minutes.\",\n\t\t\tWantErr:  false,\n\t\t},\n\t\t{\n\t\t\tName:     \"Login text template without translations for language\",\n\t\t\tTemplate: \"login\",\n\t\t\tLang:     \"es\",\n\t\t\tExpected: \"Enter the following passcode to verify your identity:\\n\\n123456\\n\\nThe passcode is valid for 5 minutes.\",\n\t\t\tWantErr:  false,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tresult, err := renderer.RenderPlain(test.Template, test.Lang, templateData)\n\n\t\t\tif test.WantErr {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, test.Expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRenderer_RenderHTML(t *testing.T) {\n\trenderer, err := NewRenderer()\n\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, renderer)\n\n\ttemplateData := map[string]interface{}{\n\t\t\"TTL\":  5,\n\t\t\"Code\": \"123456\",\n\t}\n\n\ttests := []struct {\n\t\tName     string\n\t\tTemplate string\n\t\tLang     string\n\t\tExpected []string\n\t\tWantErr  bool\n\t}{\n\t\t{\n\t\t\tName:     \"Login text template\",\n\t\t\tTemplate: \"login\",\n\t\t\tLang:     \"en\",\n\t\t\tExpected: []string{\"<!DOCTYPE html>\", \"Enter the following passcode to verify your identity:\", \"123456\", \"The passcode is valid for 5 minutes.\"},\n\t\t\tWantErr:  false,\n\t\t},\n\t\t{\n\t\t\tName:     \"Not existing template\",\n\t\t\tTemplate: \"NotExistingTemplate\",\n\t\t\tLang:     \"en\",\n\t\t\tExpected: []string{\"<!DOCTYPE html>\"},\n\t\t\tWantErr:  true,\n\t\t},\n\t\t{\n\t\t\tName:     \"Login text template with unknown language\",\n\t\t\tTemplate: \"login\",\n\t\t\tLang:     \"xxx\",\n\t\t\tExpected: []string{\"<!DOCTYPE html>\", \"Enter the following passcode to verify your identity:\", \"123456\", \"The passcode is valid for 5 minutes.\"},\n\t\t\tWantErr:  false,\n\t\t},\n\t\t{\n\t\t\tName:     \"Login text template without translations for language\",\n\t\t\tTemplate: \"login\",\n\t\t\tLang:     \"es\",\n\t\t\tExpected: []string{\"<!DOCTYPE html>\", \"Enter the following passcode to verify your identity:\", \"123456\", \"The passcode is valid for 5 minutes.\"},\n\t\t\tWantErr:  false,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tresult, err := renderer.RenderHTML(test.Template, test.Lang, templateData)\n\n\t\t\tif test.WantErr {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tfor _, expected := range test.Expected {\n\t\t\t\t\tassert.Contains(t, result, expected)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRenderer_Translate(t *testing.T) {\n\trenderer, err := NewRenderer()\n\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, renderer)\n\n\ttests := []struct {\n\t\tName      string\n\t\tMessageID string\n\t\tLang      string\n\t\tData      map[string]interface{}\n\t\tExpected  string\n\t}{\n\t\t{\n\t\t\tName:      \"Translate email_subject_login\",\n\t\t\tMessageID: \"email_subject_login\",\n\t\t\tLang:      \"en\",\n\t\t\tData: map[string]interface{}{\n\t\t\t\t\"ServiceName\": \"Test Service\",\n\t\t\t\t\"Code\":        \"123456\",\n\t\t\t},\n\t\t\tExpected: \"123456 is your passcode for Test Service\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.Name, func(t *testing.T) {\n\t\t\tresult := renderer.Translate(test.Lang, test.MessageID, test.Data)\n\t\t\tassert.Equal(t, test.Expected, result)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/mail/templates/email_create.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"email_create_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/email_create.txt.tmpl",
    "content": "{{t \"email_create_text\" .}}"
  },
  {
    "path": "backend/mail/templates/email_delete.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"email_delete_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/email_delete.txt.tmpl",
    "content": "{{t \"email_delete_text\" .}}"
  },
  {
    "path": "backend/mail/templates/email_login_attempted.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"email_login_attempted_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/email_login_attempted.txt.tmpl",
    "content": "{{t \"email_login_attempted_text\" .}}\n"
  },
  {
    "path": "backend/mail/templates/email_registration_attempted.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"email_registration_attempted_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/email_registration_attempted.txt.tmpl",
    "content": "{{t \"email_registration_attempted_text\" .}}\n"
  },
  {
    "path": "backend/mail/templates/email_verification.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"email_verification_text\" .}}\n\n    {{template \"code\" .Code}}\n\n    {{t \"ttl_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/email_verification.txt.tmpl",
    "content": "{{t \"email_verification_text\" .}}\n\n{{ .Code }}\n\n{{t \"ttl_text\" .}}\n"
  },
  {
    "path": "backend/mail/templates/layout.html.tmpl",
    "content": "{{define \"layout\"}}\n<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{.Subject}}</title>\n</head>\n<body style=\"margin: 20px; padding: 0; font-size: 12px; font-family: Arial, sans-serif; font-weight: 400; background-color: #f4f4f4;\">\n    <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n        <tr>\n            <td align=\"center\">\n                <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"max-width: 460px; background-color: #ffffff; padding: 20px; margin: 0; box-sizing: border-box;\">\n                    <!-- Header -->\n                    <tr>\n                        <td valign=\"top\" height=\"80\">{{.ServiceName}}</td>\n                    </tr>\n                    <!-- Content -->\n                    <tr>\n                        <td>{{template \"content\" .}}</td>\n                    </tr>\n                </table>\n            </td>\n        </tr>\n    </table>\n</body>\n</html>\n{{end}}\n\n{{define \"code\"}}\n<div style=\"font-size: 34px; font-weight: 500; letter-spacing: 3.4px; margin: 20px 0 20px 0;\">{{.}}</div>\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/login.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"login_text\" .}}\n\n    {{template \"code\" .Code}}\n\n    {{t \"ttl_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/login.txt.tmpl",
    "content": "{{t \"login_text\" .}}\n\n{{ .Code }}\n\n{{t \"ttl_text\" .}}\n"
  },
  {
    "path": "backend/mail/templates/mfa_create.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"mfa_create_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/mfa_create.txt.tmpl",
    "content": "{{t \"mfa_create_text\" .}}"
  },
  {
    "path": "backend/mail/templates/mfa_delete.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"mfa_delete_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/mfa_delete.txt.tmpl",
    "content": "{{t \"mfa_delete_text\" .}}"
  },
  {
    "path": "backend/mail/templates/passkey_create.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"passkey_create_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/passkey_create.txt.tmpl",
    "content": "{{t \"passkey_create_text\" .}}"
  },
  {
    "path": "backend/mail/templates/password_update.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"password_update_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/password_update.txt.tmpl",
    "content": "{{t \"password_update_text\" .}}"
  },
  {
    "path": "backend/mail/templates/primary_email_update.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"primary_email_update_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/primary_email_update.txt.tmpl",
    "content": "{{t \"primary_email_update_text\" .}}\n"
  },
  {
    "path": "backend/mail/templates/recovery.html.tmpl",
    "content": "{{define \"content\"}}\n    {{t \"recovery_text\" .}}\n\n    {{template \"code\" .Code}}\n\n    {{t \"ttl_text\" .}}\n{{end}}\n"
  },
  {
    "path": "backend/mail/templates/recovery.txt.tmpl",
    "content": "{{t \"recovery_text\" .}}\n\n{{ .Code }}\n\n{{t \"ttl_text\" .}}\n"
  },
  {
    "path": "backend/main.go",
    "content": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage main\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/cmd\"\n)\n\nfunc main() {\n\tcmd.Execute()\n}\n"
  },
  {
    "path": "backend/mapper/aaguid.json",
    "content": "{\n  \"fcb1bcb4-f370-078c-6993-bc24d0ae3fbe\": {\n    \"name\": \"Ledger Nano X FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"4d41190c-7beb-4a84-8018-adf265a6352d\": {\n    \"name\": \"Thales IDPrime FIDO Bio\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"2772ce93-eb4b-4090-8b73-330f48477d73\": {\n    \"name\": \"Security Key NFC by Yubico - Enterprise Edition Preview\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"6dae43be-af9c-417b-8b9f-1b611168ec60\": {\n    \"name\": \"Dapple Authenticator from Dapple Security Inc.\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"5626bed4-e756-430b-a7ff-ca78c8b12738\": {\n    \"name\": \"VALMIDO PRO FIDO\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"260e3021-482d-442d-838c-7edfbe153b7e\": {\n    \"name\": \"Feitian ePass FIDO2-NFC Plus Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"95e4d58c-056e-4a65-866d-f5a69659e880\": {\n    \"name\": \"TruU Windows Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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AQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAD/vR07EAAAAAAQ5G89yIURAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAACAAQEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAAAgAEBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAIABAQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgIDPI8zSFbblcAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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AQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAD/vR07EAAAAAAQ5G89yIURAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAACAAQEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAAAgAEBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAIABAQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgIDPI8zSFbblcAAAAAElFTkSuQmCC\"\n  },\n  \"90636e1f-ef82-43bf-bdcf-5255f139d12f\": {\n    \"name\": \"YubiKey Bio Series - Multi-protocol Edition\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"9c835346-796b-4c27-8898-d6032f515cc5\": {\n    \"name\": \"Cryptnox FIDO2\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"0d9b2e56-566b-c393-2940-f821b7f15d6d\": {\n    \"name\": \"Excelsecu eSecu FIDO2 Pro Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3LWFiODctOTFjM2IzZjBiNzhlIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZWMxZTg3MjEtNzM3YS0wNTRlLWEzYTktNTFkMTMzNDZlZTI5IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MjE4NWYyYmYtODVmOS1jZjQ3LWFiODctOTFjM2IzZjBiNzhlIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyMTg1ZjJiZi04NWY5LWNmNDctYWI4Ny05MWMzYjNmMGI3OGUiIHN0RXZ0OndoZW49IjIwMTgtMDUtMjNUMTQ6NDA6NTUrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/0VxRQAAGfVJREFUaAXVwXfcn3V97/HX5/v9Xtdv3Ds7JJAIAULYBZmCimDVDlftw23HqYuqPV0WtdbWR63nVG2rnraOtshDrRUfPR3WWS3KVhAZYQoEQkLWndzzN67r+n7e504iKNWO858+n2nuisS/J3G8YZeZ2ZTEImD85+ROO0ZSUfiHJP6FHyIEWBjAwzNw6obI3CykCGaGJNyhLMWwgnropNJICBNUcooi0O8b+xfF6PLAqIMcGod2W+zYD9Fg49rAgb1i0TJTHWGCuo6UheEJdi9mVrSN8cKYq42d+8SKCSO2gAwdIBQQTPx7ZlDVdkkWbzTZcKTI3dhvvrGlueM9d8UTX0Rr+jmoyYCQOMSsBLpAAjLQRxpgxo+RAmlr4ocIZheGkF5lBpL4rwhICXLDfH+gDxeFkHgCCeSwf78hEz/KjMPED5IgRXuRuf20pYBZQ72f7StGH3YmTvxFMhcgAwliARLgGWwGNAfWQqwmhshBcn4sGOA+l8qCxxmQBU3DSZIj8V8TYFC0jYUFbe31dP2y5ZAzTxAS5MZAgPGjzQBB1YDxA9ZZ0KkmcEHImc93Lvi3HfHIkqZejTIgMEAO7l8nxk8h3YLn3YQ0jusM1LyOEM5E4seCgOz/lPYcEI9xQTtxxHg3nukYIL5rEdgOCCj4fgYSsR5qRaejq0Jiuqp4ghQNLw1V4seFAK9FMr5HQLTjQgybMciNg7Hn1pWXfOOh6sSL8PkjMQdLYGGawd7fJXYvR0WfEMAC1BWE4lZ6C/9Mmf6OcuTpSID4kWUG0m7Evem2bc5jho1YOxmPOnMTp2aJ7ICBiY8J/T7QAkYAcZAAQ8Eoc0O2yLbRUUMCM5CMdhv2zTlkI/JjRGARQhHIjXiMGcdKGneM0jKIOx6pV+/LZucj7xAMSPvo6xV49QXSOMzNw8gEdFowMwMjY5DSXprmrRT6B4xViB9dEktuJNqOtHc+8Jj+EDpd2xTajGgAGeMgd/9nYE8I4IIQQCwJgIMLXBANmgySkR2K4Nz9IDw6LzYfLQrjx4YZNDX0ek53LCBxSAp2jplhghY1szZx01XNBXMEthAqQBW95h006QvEEahJtMuXUMQX0FRX02p9hCLNowCersf8PrBV/KfEYcZ/nzjM+AHuEAL/ITlgYMZhBq6bEQvpSUdGHlPVxBVjdo6y4RIgENsEO6JBlpECVLUTghFLQTYcIyMKQZMhG1QNFKX45j1iYtJoJUOV+CEMGAECMA+I/w8CXGCAO1jkv81YIsgOEoeIwyxAXYm5/c6qlYZnaDJH5czJhIBMmOAh3/jlgXVWQz6RYDAYXstC/Rd0lkM5AvI3UHTfRwBqfx4jo1uBL2IR6gDZG0IABO4QI2DgDiYOsQRykIMZP0jgGULicRYAgQvMOEQCMyha4BnkPIEEFqBoQa7AHUIEBDnficjppElxiIDIms6YnZkbaDJYMDz73cgfmWkCRYLJCP0+WAAKHmeAZEgQAgTjkNE2pAgShwjIAozjgZ9BOk+wzsBc7AO+gvikxKP8JwS4GDG4KEXOEqzqtPAA3zHjC4Kt/BcEy4Jx8WibM2JkKooaeAD4CuLbGBQlxBEjZkGf9XVtm4hgCIzZv+XFDz0YNp6NLaxEDmXns0yZEyoo0xnI/oicoakhRMBeg3wTUkn21RgnE8QhrQ4og2cHbQf24qwi2HqSBRqBADMe5w6pgM4YDHqQGzCDkCAVMOyBHCwAAgGxADl4BoscZqAMCGILwjhUPaFswA6C7mFJmnlUHOQZWl1Wj4yyRUEgkBtlyT2tqAN754W5sWRCcKrgDLDjgOUGCoGdGLcC/yp4hB9GEOCYqXZ4bW7sRdF0FGaGIAMpQsCeZYFfM7N3CP7aQHwfATmrRPZLrcivYGyWWVeCtZMgl5rK3pSiPobzh8CA7yMgi1GZXepur4zGpg2rYlnXAjeUhDsPWeTPLfLH1UDafm+mLoyRtv3EZNcmqyxaNCBuvT6euwPxMtRv4+rRG9xIMug0MNQBLNxPa2QLuYFqAMTnA8/noCIAxiEhgucDLPY+TjP4EuNj9+DWJ4RANXM6dN/CyLKzWJwFbyBEQBBLUIDFmQdxXUcq7sTCgGH/KPpzz6AzehIGNA2kNnjewfbbPsrY6vtoTz4fa16IBcgZWiOQ60fYfv+HmFhxB93Rn8Pzy3DdjrGdJam7MXCQBEXkDDPGcgUWwXAGfV1fW0Buay3y87g9v922Ew1bITcwgSAFQ8Jj4H6ZXVFLHwBm+S4HArx49TJ7R9kKxw8WwQKPk6BsQQGWzdYXo/GjdZOjMh82DpMgJjtp9UT8391kF+eGokjCJbIMlxBYrnVku2tvMw9HmvJrBQOWOFAETlnVDh9sWbigccNM1BnEkiAkkLEhBHt3GWwVmd+8d5vzxe/E9Myz7cyLz4fqESiV2Vls+PyeYm2PPk/FMsgHDPozWICqgm7nATy/gNk9r6Eon0d79Ek0FYcICAHEEoEPv8qjD7yTVcddw8R4QzWALBBg+WFmFr/KbHMFU+XzCAmygwUo0x72PfSXPHDn37LlKQ9h1idEwGFm1yo6x7yVsvtG6hkwoDP6NhZmLmfZxhYpXYzXIAGCaCC9i179FzTXQTrhQspN4IvfAuZZkrpdcZCgE2VnezZcImK0Onx1dtb+Lje6eNUK+2DCjq9dhBC05ADSiAXKVjSaRjQixGDHgr3T4FnAr0p82wWdyFtbI+G3TTbeuBAQgBAN5PMjLT53x4O6etsC+84/wdZOYi9tiO8yy7ci3chB4txWyz4S4cQiQOg6vR57TFyVgjyYXSRY1QAOdGJ8qaRrJPtoU3PQuSnYFaPRNmWDjDDYWdV+vRnZ4Gwz22BANZSVnfiqo47ls5POVfPLbO2KUdtMX2AGBQw6E9c0d+1dxdrjNtFOoDhCZ/957HhgK0efC6EG5x4Gi79OSh8gpKcR/dcou6fQn4fskCJQ/z3Ub2BqzU6aPowsO5bh4AJcu/Dmq7QnBvSZZ/vWtzN27Gl0JzcyWATZ9VRzb6bdvobN54qiBWqgGoIitEf3sOfAmxi3SLd9KVV/F63uVzj6LIjFOlRdgAUQEAMMq3vJdhVr1kJuLcMmn4oqoL4ZPIORGHCIGVNEThJgBtn9y8MBrx8ds7cFhXd2ohg2fmPO+nSQ3Qy2D9NkU9kpi42/oGyFi8pIkAtvxMSYnR+K+AkLzYtG23ZBuwxvyz2160aYQZFAUPV7/qmisD9nVLf1+vSne44sQNYVjeztpfHURn4TsM4svM/EiSHBTF/9hUX707Ktj4602IXIN9zVbJ4ai+/fcnS4sBqIxlW0Y3zdvgU+um3ajzjtKP4MbFMtkGnOs783hPDJEOxRSRgciXgbxksFlqKtaKf4wv5QV516rJ60yjmh2m9YEJTsfo9e/8h9BzaewRHzU4QCFFqE8Aa8uomiuIWmD56hLMDig7RHHuSWa7/EsP9RTnn6s4gGi/W1yN5IHOykM7GMhYU3s7j4UsRqilAgPk6Ov0673stR628nhxvI2kh3/CbmF1+LuI3xNeDh6VT9VyGORPlmGv9TJlbtxID54V/Saj8XfCdzexexNtTVWUTfgBmYQTDoDXfQ0zYmWpA2noP7CfhgHyHfjomDkjjMxPpAOA4Dz9wg8X7V+r2RTnz5Yq0Hds/lPxwp7TPBmOO7gkHlXHv3w/6xiSn/+VM2pbdXs/Ykj2I4EKEKW556UvHlmJioemorc0grQQOPHhj6W2nsb8qCx8UIMRi49tdZf1AUXDBWpomFSr9lFs4JCAvM7Zr1S/vzfHzDesMMEDRut873mrcop/cEWB8DzXRP93/qOi/OPzn9amvUnrwwC5ge8tpfBXyNJ7ob9DuYnWjYaZ7FYrZNMcNK2JKCjVdmdBnAgBsf0hHb2LLudaQDI1QVyKCz6mSOmfok7n+M/Et4/QitUeiOgzcg7WDY+z1yPomiXE9jf4hpB6b1pHg54yufwXAAZhANXC+nam4l8B6649BKB8gLMNd7J5Vuo4qREbuMwcJvY2EMi1CMXoSqDthlxAAdzdI0eyk732I4nOOuu2H96tNZtTwxrCAYxAQL+2/CrM/oauhVT6ZVdJhurqetA3QiOKQUje86xYwpwU7Hr20ne0v2dG4/6+vu/ipgG99lgFhiHNI4vUa6HPdv7hvwibFOODUBuRHjIxyRHeoGgkEMsGtG387B31h27GoJEODQbUO3Mu7dnlnZEWXBVLsdO5Y5Xh5eoCiKCDNz+UPT+/zjrZSQwIA6w9pJZzD0awfz+eeSaSwmcpXZNTVqp69ZYb8iB8+OR96dUvxaMEYlGWBLWJKBA3J924zTWOKoXDSnK9uYJAQEgwPN6NW7e2ugzdmQQSwR4NDubMb9r8jFVqI+AfYZot+H+nD0aSz5Bsq30BvsgvANmj3gfhRh+TShuRJ5BYiGAhgh6B6KBAasWH46X7/yc1jrK+x7ADY+8+XE+AcIwwRiSYZ2+UtIZ1A3MxRhAmkzln6fbdsaRIeiOJWDDJBDw4D22LcY9mB2DkJ6MrRgqnMzTX2AbByUkFjSwux0CQyfjm7PDeNh06DUF1p9vZzGpuWAQAYZMMAM3CEA3TZQsHWu1s/UMf/VUd1wSb+GQQ0GmEGIQApff3R/fu3KFdzlAjNQgGYIJ22AZpv40OfhwjMDzz3dLt25x+Ro4+rltiwPIXS4p13yJ1PzRrsFqQV1AwZ0S2M4BEk7DJFlrBiNxYvP54VkVizOiZBsEemngLME44D4nhooDM7iIAODxWgU0ThJAtwgwZfjJXdsDSe2CPkIVAMBMBDQDDkkdU7Euu+iHrwaeAmTozfgwGIFqIf4BKVP0x9C5jq8uY5Q8D3GIcpQlNCdWMnevcv49rc+yrLOIivXrmCyuIzKDRNgPK7JXeBczMAdsPsxu42NR4H78ZThFOoKMEDg7GB0fCsR2Lv/BI5YtxkL8J0br6O3PxMLDkpkDpqk0OkgYrCjrWMj9+3RTdMLevU4TK8eg7IFbpANhAhBWANmcMRyY6SA/oLYvMy31zle2Wu4hCXGYWZQNf73/YpLy5Z2lQFKjNACBehV0CmEAAdiyXndbnrp1unmj8pRzl7fsnbdwM55v3rdlvDoyRsMGjHYATPT0EqwcsKwEFEw3CCHQITV0eyiWuAGEUbKEH7aAQnMDAQOGGAsCYYAA5R9ayfY6Ql7umSU7RrmeHB7/aTbB1Pd55B7G3DLYLs5rA02AUTUgAtSsZHsL2bPgRtoHCxvAFtDsK0YMHlcC08ryL2E6hqL4qAQurgmiUXBsP8wvdYrqPbMsn7l1Zz6HFi25kJy3shgHkLgCQwQICAVsDB7Lb3eblathRBPYXbfCg6yCFZA/5E7Ge6+ndFTYM2G0xlrH0Nv5gBX/eO9PHw3dEY5KClw0LGBcCoYoJFOS+zcmT+9Y5e2r15hdDvG2nFjUIEBBphgUIt2aRy5yrh9u5jtiRPW8Ryv7HfdjIB4TDDDG3v4zl3DfWunjNFWoh2MJkLtEIEA9IYwVjK+6aj4f+gqnLZJN2XF1wzmhRVUDNnaTAMm6gXRzBmt0pA7VQ2rlhc0bmQXMQnPrOkNOc6CiIYHWBCqBMkMY4mExYAlo19l9Tms7WbT9dA/VrTt9BitW1XQsQyJ665ZPHUHzs9igxLxBoyrgQI4HvQBzKZwQVmA5Dy86yYqwfIWdOIFMHICsd0DQTVYhzVXgE1BmAVzzEaAI4EaYz/YDKk6FzpXcMHPPkznKCCtp9ofeZyAwCFyiAkCmeyR1LqdXPWY2QNmJ5DKhDtYgPbYkMXZ/4tFiCuAAz9BM4R+/0Y2n7OLdcdBKjkoyQBjM9A1RBbUiyyun7C7jl4LT1pjzC7AYAhmPEEwkKBqIDsEC78I9qc1jEeE+B530WmFX142mu6qc/6wAxlwAQYIqgxjHVa88qJwxUmrwmmPPly/eqodDySz5XUjYm3FiraWz+4WQSKZEVqgisMETaOOjGyoaHfFcNFGlBkLLDELg+x/Hcw/UgQ7KrsiQg4qZHm20e6W2ZxxSLdpvJ2d+wrs9TlDLA0GkUU1dzQTu6DiGJLNY3wWtA0MpPuBS8HOBYEE84t/QtH6OKuXQf9R8PZTaY+sYvb+BYYzMPKkfRTlPmI8HxzMQAb14MsEu5JQ3IL7y4iD80hjs7hVTO8B91tot2pSTMhABjSQ/XMU5VfBd7M42EIIl7Fm5RyjJXziz6CutvPcN2R6/UTTh8X9H6fV+RuqGaA/Tq5+gl4FqfUNLvz5/aQCJA5KJloW7GQzQxImY+j61oYjuNbN2DcLGJiBeJwBJTB0QQrW3bDC/qAswpuGtSXMOcjEfhkdoCPAXWPHLEvvne9jcj5iAee7hKhqe8bxa8L7WuviKffdnR/+5j360nOeTphMigxAYJV4aoxWFoTKlUEGBnII0X7ZjJcHVAmb2D/jfzbRsu8oWd+zuskgi/Yg+52jId6JGWYQgeyBPZXO3dANFwfRdTEm+TtapR8RzJ6R3eh0wfY3fGbfebddc+zLVlFrI4OqDWqDwAKgA8Bbwf8nKQVC61NUM59h1SS0OtAfvZii9QJMsLhtGckgNnNQ/jLKd0A8h5AXqPt/D91PEFOmGXYJcRliiTajZgr3abJdh/ROxG+hPEWIcyi8H5p3I1+kbqA//B3WroU7bzjAo/fD1BGw7bZPM6yOpCjOoan+lf7sB2lPQQR6u09gZORkHDD7JtUQqiGPSRaYDGZPFocZwkyr+xW/GQwrjEI8rhWMZYKVwOddfMhd58TC3rlqMpxfu2gaUQSjct0WsFcX0iuaaJfKRRa0IqNlN35g6P6zLn0O7CGDo8GeEYM9nRDG6LnPzuc3bZzioeZAXqbxsK1VhOXDSpjZBaXCR8z0Boc5lrizPJq9vSzt0ioTOy1jUGn20Wm/u73Btrfa3D+YtZOzYDTZa3pVmBs29rutksrMkBhPQb+4vh1+TzBlBlm6y4y3J2OF0BaLRr2YSSV3PbjqKV+bmVv3U8TekZgD8dm4303OEAOY/RuR62m1CtA81X4IU9BUmylb78fKZeQ+LH/yZRTDW6mb/eDTiLeT2qMMFobM7x6y+hTIfjTW/zgxnYsDFi6iGZ6C6d9opYzxxzS6imZwBGOj91OH2/DgZIdW+fsU6e20OrDnoROpdSWnPg3WbNpHtrexsDBCqzXHyCQ0DiHB/PRGxiZXYPVecvMQMr5fGhnV+oV5Oy1EDnFA2HGlwluiAcZhxiEu7TXZfULHhEKXE3ha5ayihmhGA9RZ/+TGb7jn78j9ESxeHCwcD2KYRTArkoXnuPjJAH2DtoKlgiUyWPRLJzv6h1gEFqfZ/8h2/c0Jx3NqUZJyA2Z6hdAWI/yrRLdT8EzHNsug0zKiaWeKegnGLQMpDOa5ciTYybULi2bdMv5GnXWhYVeDumZ2tsxOG41K2aGW3SDpJRY0INh5YAgDBwL3rIr7Fqk4DUtgBjG+mex3In0RM8iCfjNgcGDA7COQa5C9iFi8D1tYj9cgQWfiEurp9+LVH5HCvZg5+Bz9Piz0l7GOX4D8FhpbjsQhRiIW76YZ/gIp3oXUYM31pBLm52FQQXtqPa3wv5C/FDOYmYbTnv3bxPYOegsfYd2xMKwyg2qelj2bOh+L6y9ot0RafRG5BuVv4HoYxPdLuw9w3nhbHXcwQIIiQpFgWAl3sMAQ8Yjg9ib7rkQYiYU9H7N1LhEEjXDQ9YtDf380PtNqBc9AI+0I2X8ppXC5sGMdIQlxSBSMGlCYMWg0bda8voU+7dnwDJ0Iew7oY2saf9rqkfhzvVknm8zgzGDhTAEREYNRZdEfautYl1enxHWGyAfcLdtfxzF7Vtm28/p9sSSmZOe4cw4YBzlGPwt3/5cQwpswtg1rJmIRnhmCgaATKmY0ddvn9TwoOQvmOURaTQyXI/8Y8FVcDzB0GM6vYzg4hbXHP5MmP5O8WBITh5hBNQ90foGyfSGevwi2C29Ed/xIyvYFDBePBkpCAnGYZ7B4FmX7M8DloOsw7Samkrn+MXj9FLrpeeDH0TiYgWdojXao6/cSeDbD3q1kb2iXx+P2XFKMiJ8m2DixPA014NxMtlmMJ0jb9tnZZxxnDOfkBBQCw2GjhcVK02WyngVlyeYxTHBcCuECC4zWWVni3mS6rwjcOZe5vsq6Osr2SeIxBpi4buD5xQG7LJm90MFSMCRwiSLSm6n1jwuV3ruyxc0skURrMtDpGidMsZCC/aqyzwq9MkUrzI1GAoxa0E7a45Wu7A/1J2PdcD8CBKpEu9SOnMPL983z5xNtPSsRGGYoAkjgEgm/Z99QHy4jl3eD7R9UjmACOBWJQ8TiPlv+2ft13BbE6YQaCDXuhtkaiuLNoNeQwn5GCqNYPsmyI8aIRaLuQ64bQiEQhxlgEexoTK/joJyh1YGRSRjMC1ETAk+kQExbUH4XhBkIs7hKppYvw2wEr1nimDWAESIMemA2SozPR/58YoQEuACDYJcgB3OWOHAdQfx7afPq8MFqUZ/EaEAKwRZ7feYXKy0eudKyGpsaVkzGSNtgBOTIpptGM2ALKXEAmHfRuKBgifFEBln6lsP/kOuKYPaUoeuoEGwYpHvqxr9eK9zkMDS+TzSsMDoJAuz2rDcOh/nvKsVnWNDxLQiYpt11izJfk7TVzDKPMSAABiHw4N45veThPf6TW9bylLJgw6DCzNiZTNeY+HqWHhLG9EJN3YiU7MBIaa8RgSAlEotfqJ91813941fQ7b+SQMZVAYZkmLWRuhhtygQh1BiLVIsDjExIgPNEDQgDEpAIBrluyE2DmTCWiB+gJgAdjBHMEpKIcQj0aOohZg4YjzGWyJAiUCAHUQMNB0kRcEQbbBa4iR/i/wH3D5PMpd2t5QAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"c5ef55ff-ad9a-4b9f-b580-adebafe026d0\": {\n    \"name\": \"YubiKey 5 Series with Lightning\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"2194b428-9397-4046-8f39-007a1605a482\": {\n    \"name\": \"IDPrime 931 Fido\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"39a5647e-1853-446c-a1f6-a79bae9f5bc7\": {\n    \"name\": \"IDmelon\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2YxNWI1Yzt9LmNscy0ye2ZpbGw6IzkyMWIxZDt9LmNscy0ze2ZpbGw6I2VlMzAyNTt9LmNscy00e2ZpbGw6I2JiMjAyNjt9PC9zdHlsZT48L2RlZnM+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzQxLjg3LDEwMC4ybC00LjI5LTEuNjRjLTMyLjMxLTExLjgxLTY1LjM2LTEzLjI3LTc2LjkyLTEzLjRsLS44OSwwSDEyMC4xMkEyMy40MywyMy40MywwLDAsMCwxMTEuNiw4N2MtLjQxLjIxLS44MS40Mi0xLjE3LjY0bC0xLjg1LDEuNzYsMTMzLjM1LDY1LjgsMTAzLjM4LTUyLjg5WiIvPjxsaW5lIGNsYXNzPSJjbHMtMiIgeDE9IjI5NC41OCIgeTE9IjEzNy4wNyIgeDI9IjI5Ni45OSIgeTI9IjEzOC4yNyIvPjxsaW5lIGNsYXNzPSJjbHMtMiIgeDE9IjIzOS41MyIgeTE9IjE1Mi4xMSIgeDI9IjI0MS45MyIgeTI9IjE1My4zMSIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTEwNi43NCw5MXEtMi42Miw0LjItMi4zNSwxMS4yNnQuMjYsMTMuMzdWNDIzLjIxcTAsNS43Ni0uMjYsMTQuNDF0MS4zMSwxMi44NGExNC41NSwxNC41NSwwLDAsMCwxLjE0LDIuMTlsMTM2LTI5OS41NkwxMTAuNDMsODcuNjVBMTEuMjQsMTEuMjQsMCwwLDAsMTA2Ljc0LDkxWiIvPjxwYXRoIGNsYXNzPSJjbHMtNCIgZD0iTTM2MS44NiwxMTEuNTNjLTIuMzItMS41NS00LjctMy4xLTcuMTMtNC42OGE5My45Miw5My45MiwwLDAsMC0xMi02LjMyYy0uMjctLjExLS41NS0uMjMtLjgzLS4zM2wtOTksNTIuODlMMzg3LjYzLDQwMi4zMUExNjQuMDcsMTY0LjA3LDAsMCwwLDM5NywzODguMTJxMjkuODItNTEuMjEsMjkuODItMTI1YTI4NC44MywyODQuODMsMCwwLDAtNy4wOC02MS4yNSwxNjQuMTYsMTY0LjE2LDAsMCwwLTI2LjUzLTU5Ljc1LDEzNC45LDEzNC45LDAsMCwwLTkuMDUtMTEuMzhBMTUzLjIsMTUzLjIsMCwwLDAsMzYxLjg2LDExMS41M1oiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xMDYuNjUsNDUyLjM0YTEwLjA3LDEwLjA3LDAsMCwwLDcuNjksNS4xOWwxLjc0LjJoMTU2YzUwLjI3LDAsODguNjQtMTguNjksMTE1LjUyLTU1LjQyTDI0Mi44OSwxNTMuMDlaIi8+PC9zdmc+\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2YxNWI1Yzt9LmNscy0ye2ZpbGw6IzkyMWIxZDt9LmNscy0ze2ZpbGw6I2VlMzAyNTt9LmNscy00e2ZpbGw6I2JiMjAyNjt9PC9zdHlsZT48L2RlZnM+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzQxLjg3LDEwMC4ybC00LjI5LTEuNjRjLTMyLjMxLTExLjgxLTY1LjM2LTEzLjI3LTc2LjkyLTEzLjRsLS44OSwwSDEyMC4xMkEyMy40MywyMy40MywwLDAsMCwxMTEuNiw4N2MtLjQxLjIxLS44MS40Mi0xLjE3LjY0bC0xLjg1LDEuNzYsMTMzLjM1LDY1LjgsMTAzLjM4LTUyLjg5WiIvPjxsaW5lIGNsYXNzPSJjbHMtMiIgeDE9IjI5NC41OCIgeTE9IjEzNy4wNyIgeDI9IjI5Ni45OSIgeTI9IjEzOC4yNyIvPjxsaW5lIGNsYXNzPSJjbHMtMiIgeDE9IjIzOS41MyIgeTE9IjE1Mi4xMSIgeDI9IjI0MS45MyIgeTI9IjE1My4zMSIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTEwNi43NCw5MXEtMi42Miw0LjItMi4zNSwxMS4yNnQuMjYsMTMuMzdWNDIzLjIxcTAsNS43Ni0uMjYsMTQuNDF0MS4zMSwxMi44NGExNC41NSwxNC41NSwwLDAsMCwxLjE0LDIuMTlsMTM2LTI5OS41NkwxMTAuNDMsODcuNjVBMTEuMjQsMTEuMjQsMCwwLDAsMTA2Ljc0LDkxWiIvPjxwYXRoIGNsYXNzPSJjbHMtNCIgZD0iTTM2MS44NiwxMTEuNTNjLTIuMzItMS41NS00LjctMy4xLTcuMTMtNC42OGE5My45Miw5My45MiwwLDAsMC0xMi02LjMyYy0uMjctLjExLS41NS0uMjMtLjgzLS4zM2wtOTksNTIuODlMMzg3LjYzLDQwMi4zMUExNjQuMDcsMTY0LjA3LDAsMCwwLDM5NywzODguMTJxMjkuODItNTEuMjEsMjkuODItMTI1YTI4NC44MywyODQuODMsMCwwLDAtNy4wOC02MS4yNSwxNjQuMTYsMTY0LjE2LDAsMCwwLTI2LjUzLTU5Ljc1LDEzNC45LDEzNC45LDAsMCwwLTkuMDUtMTEuMzhBMTUzLjIsMTUzLjIsMCwwLDAsMzYxLjg2LDExMS41M1oiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xMDYuNjUsNDUyLjM0YTEwLjA3LDEwLjA3LDAsMCwwLDcuNjksNS4xOWwxLjc0LjJoMTU2YzUwLjI3LDAsODguNjQtMTguNjksMTE1LjUyLTU1LjQyTDI0Mi44OSwxNTMuMDlaIi8+PC9zdmc+\"\n  },\n  \"664d9f67-84a2-412a-9ff7-b4f7d8ee6d05\": {\n    \"name\": \"OpenSK authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"3789da91-f943-46bc-95c3-50ea2012f03a\": {\n    \"name\": \"NEOWAVE Winkeo FIDO2\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAACqUlEQVRIx2P8//8/Ay0BEwONwagFpFlw8cKFirIyR3t7S1Oz0KDgBfPm//z5k3izvn39lp+Ta2tltWTRIoTofxhYtXKllpq6srwCAikoRIVHvH379j9x4NSpU0AtQI1W5hZwQagPzp87V11ZiXAvIxj9Zzh54kRNZRWRPvj96xcDOM0zMTKiB9G8uXP//fsHNFRASLC+sXHm7Nlubu4Qm3bt3Llu7VpiLGCEmcuIacGZU6fB4cWQX1AQGx/n7OIyaeoUbV0diIvamluePXtGUST/+g32HSODhoYGRISFhaWppYWVlRUo+OHjh6b6BoosgHvqz58/cDl9ff3M7CwIe8+e3atXrqQgmeIokDKzs/X19EGy/xk6OzofP3pEWUbDsAYYRC3tbRwcHED2h/fv62pqCReOjCTmZE0trZy8XAj78KFDy5YuJd50VAsYcepKTU83NjWBqOnu7Hxw/wE+O/7jsgC315mZmRubm9nZ2YFqvnz+0lBfhzOg/qO7lQm/B+EAmHwLioogCo4cOrxk0WIiPUEgkpFBUnKymZk5hN3T1XX3zh1iYoKJcDTBA4qFubmtlYubC8j++vVrTVU1qHQhzQeMBHyhrKxcWFwMUXn61Kn5c+dSv8JJSEy0trGGsCf099+6dQsuxcLCCrH7P5IrSYgDeKFS39TEx8sHZH//9r2uGhFQN65fh2VPNoqqTCUlpeKyUmgxfPpMSWERMAMuX7asv7cXIqilrYXwFrxeg/qOuGZSdEzM3t17Dh06CPT0pk0bN23cCI9FYKZJz8hE98Hff38hDDY2diL90dHdpaurixawrCysre3tunq6iLTX0NAAToIsTx4/tndwiIyOAtYExFjAzc3t4+sLJL99/QosE0VFRe3s7RtbmoGVFUqcjTYdh78FAIhBLlNd7ju1AAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAACqUlEQVRIx2P8//8/Ay0BEwONwagFpFlw8cKFirIyR3t7S1Oz0KDgBfPm//z5k3izvn39lp+Ta2tltWTRIoTofxhYtXKllpq6srwCAikoRIVHvH379j9x4NSpU0AtQI1W5hZwQagPzp87V11ZiXAvIxj9Zzh54kRNZRWRPvj96xcDOM0zMTKiB9G8uXP//fsHNFRASLC+sXHm7Nlubu4Qm3bt3Llu7VpiLGCEmcuIacGZU6fB4cWQX1AQGx/n7OIyaeoUbV0diIvamluePXtGUST/+g32HSODhoYGRISFhaWppYWVlRUo+OHjh6b6BoosgHvqz58/cDl9ff3M7CwIe8+e3atXrqQgmeIokDKzs/X19EGy/xk6OzofP3pEWUbDsAYYRC3tbRwcHED2h/fv62pqCReOjCTmZE0trZy8XAj78KFDy5YuJd50VAsYcepKTU83NjWBqOnu7Hxw/wE+O/7jsgC315mZmRubm9nZ2YFqvnz+0lBfhzOg/qO7lQm/B+EAmHwLioogCo4cOrxk0WIiPUEgkpFBUnKymZk5hN3T1XX3zh1iYoKJcDTBA4qFubmtlYubC8j++vVrTVU1qHQhzQeMBHyhrKxcWFwMUXn61Kn5c+dSv8JJSEy0trGGsCf099+6dQsuxcLCCrH7P5IrSYgDeKFS39TEx8sHZH//9r2uGhFQN65fh2VPNoqqTCUlpeKyUmgxfPpMSWERMAMuX7asv7cXIqilrYXwFrxeg/qOuGZSdEzM3t17Dh06CPT0pk0bN23cCI9FYKZJz8hE98Hff38hDDY2diL90dHdpaurixawrCysre3tunq6iLTX0NAAToIsTx4/tndwiIyOAtYExFjAzc3t4+sLJL99/QosE0VFRe3s7RtbmoGVFUqcjTYdh78FAIhBLlNd7ju1AAAAAElFTkSuQmCC\"\n  },\n  \"fa2b99dc-9e39-4257-8f92-4a30d23c4118\": {\n    \"name\": \"YubiKey 5 Series with NFC\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"341e4da9-3c2e-8103-5a9f-aad887135200\": {\n    \"name\": \"Ledger Nano S FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASYAAAEACAYAAAAeMdvxAAAAAXNSR0IArs4c6QAAAIRlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAAEsAAAAAQAAASwAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAASagAwAEAAAAAQAAAQAAAAAAe6SCkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KGV7hBwAAD65JREFUeAHt3LuOJGcVB/Bd9mIHNhLiIhOQOEaCCDkiICNG4g38CjwJCQlCBASIBN6ChAgJJERiJAvZAoyxfFnvhe/s9JFqe3tmuk9/p6d651fSN1VdVedUza9q/l299sydO3fuvD/GszGebOaxbKzX4NHm+vxqzGN6cDHzdSFwf7P88zGPeznN3Nfrva/j2jzdXK9PvzIWTAQIEFiVgGBa1eVwMgQIhIBgch8QILA6AcG0ukvihAgQEEzuAQIEVicgmFZ3SZwQAQKCyT1AgMDqBATT6i6JEyJAQDC5BwgQWJ2AYFrdJXFCBAgIJvcAAQKrExBMq7skTogAAcHkHrgtAvFLoqYzERBMZ3KhFqd5d7Oc88Umi5cIhBWvS3DWuDr/PMQx5+ad6Bi9w2vTO+eHd7g9FWmUf07j9nznN/+dHvVGEMXx95i+PUZcvH2foPKCR/1Px/jjGG+OEX/T6agTGvWmqwXC/t4Y/xkjrl145/UYi6YhkCZvjeVvjPF4s27MTE0CcQ/Gg87HY3x/jN+PEVOs3zcTct/PZjwx/WUc+L04A9PJBfIH8OQHXvkB8wb/5zjPGKbTCjw89nAzgumNzUnEycQTk6lfIAIpnnBjmHYLRDjFJ4AYsWzqF4i/pvr5GJkJ5SPOCKYMo5jncvmEFBKYKCC8J2Lu0So/ssVH56Omff9N6aiDKCZA4FYJZECVv2nBVKZTSIBAl4Bg6pLVlwCBsoBgKtMpJECgS0AwdcnqS4BAWUAwlekUEiDQJSCYumT1JUCgLCCYynQKCRDoEhBMXbL6EiBQFhBMZTqFBAh0CQimLll9CRAoCwimMp1CAgS6BARTl6y+BAiUBQRTmU4hAQJdAoKpS1ZfAgTKAoKpTKeQAIEuAcHUJasvAQJlAcFUplNIgECXgGDqktWXAIGygGAq0ykkQKBLQDB1yepLgEBZQDCV6RQSINAlIJi6ZPUlQKAsIJjKdAoJEOgSEExdsvoSIFAWEExlOoUECHQJCKYuWX0JECgLCKYynUICBLoEBFOXrL4ECJQFBFOZTiEBAl0CgqlLVl8CBMoCgqlMp5AAgS4BwdQlqy8BAmUBwVSmU0iAQJeAYOqS1ZcAgbKAYCrTKSRAoEtAMHXJ6kuAQFlAMJXpFBIg0CUgmLpk9SVAoCwgmMp0CgkQ6BIQTF2y+hIgUBYQTGU6hQQIdAkIpi5ZfQkQKAsIpjKdQgIEugQEU5esvgQIlAUEU5lOIQECXQKCqUtWXwIEygKCqUynkACBLgHB1CWrLwECZQHBVKZTSIBAl8D90fjLTfNHY35vjGeb13d3LC/XxW4PF/vEa9PpBOJaPBgjr9chR87rmNf+kFr7ErhOIO7JvLfy/sx7LmqXy8vXse/zTIov34wtY3r9Ynbw1/jhMJ1WIC9svJmYCKxFIO7LmCJXjsmFr0aDX48R4RQ3+b4f7TIF4+AfjBFTrrt45WuXQIbSt8YBfjzG48WBclusyptkeV1ye1z3/47xhzGejmEiMEMg76V/j2a/3TSM+y/vxeuOEftGBn1x3Y77bt/3wPv2s9/lAvFxO6YfjREXsjo+HLXxUTwm1+/CwdfjBabcS/HOGQl1TLNIyfjhMJ1WIJ+U4rN8XL99r2Fcr3jS/WgM120gmKYK5D2Vb6CV5s8imPIdt9IgavJEqvXqjhOIG2DfUFrut+/H9uPOTvVtFciPdaXvP4OpVKxoVQLL0LnqxHK/nF+1r20EqgJHPbB416yyqyNAoE1AMLXRakyAQFVAMFXl1BEg0CYgmNpoNSZAoCogmKpy6ggQaBMQTG20GhMgUBUQTFU5dQQItAkIpjZajQkQqAoIpqqcOgIE2gQEUxutxgQIVAUEU1VOHQECbQKCqY1WYwIEqgKCqSqnjgCBNgHB1EarMQECVQHBVJVTR4BAm4BgaqPVmACBqoBgqsqpI0CgTUAwtdFqTIBAVUAwVeXUESDQJiCY2mg1JkCgKiCYqnLqCBBoExBMbbQaEyBQFRBMVTl1BAi0CQimNlqNCRCoCgimqpw6AgTaBARTG63GBAhUBQRTVU4dAQJtAoKpjVZjAgSqAoKpKqeOAIE2AcHURqsxAQJVAcFUlVNHgECbgGBqo9WYAIGqgGCqyqkjQKBNQDC10WpMgEBVQDBV5dQRINAmIJjaaDUmQKAqIJiqcuoIEGgTEExttBoTIFAVEExVOXUECLQJCKY2Wo0JEKgKCKaqnDoCBNoEBFMbrcYECFQFBFNVTh0BAm0CgqmNVmMCBKoCgqkqp44AgTYBwdRGqzEBAlUBwVSVU0eAQJuAYGqj1ZgAgaqAYKrKqSNAoE1AMLXRakyAQFVAMFXl1BEg0CYgmNpoNSZAoCogmKpy6ggQaBMQTG20GhMgUBUQTFU5dQQItAkIpjZajQkQqAoIpqqcOgIE2gQEUxutxgQIVAUEU1VOHQECbQKCqY1WYwIEqgKCqSqnjgCBNgHB1EarMQECVQHBVJVTR4BAm4BgaqPVmACBqoBgqsqpI0CgTUAwtdFqTIBAVUAwVeXUESDQJiCY2mg1JkCgKiCYqnLqCBBoExBMbbQaEyBQFRBMVTl1BAi0CQimNlqNCRCoCgimqpw6AgTaBARTG63GBAhUBQRTVU4dAQJtAoKpjVZjAgSqAoKpKqeOAIE2AcHURqsxAQJVAcFUlVNHgECbgGBqo9WYAIGqgGCqyqkjQKBNQDC10WpMgEBVQDBV5dQRINAmIJjaaDUmQKAqIJiqcuoIEGgTEExttBoTIFAVEExVOXUECLQJCKY2Wo0JEKgKCKaqnDoCBNoE7rd11vgcBOL6Pxnj3hjPzuGEDzzHp2P/GKYzExBMZ3bBJpxuBlAE0mebfq/yD+/d8T3m9zyBT4tTCAimUyiv6xjxgxrTm2P8ZIwvx4iP9K/SD298L6+N8acx/j6GcBoIJgKdAvGxK6YfjhE/gPHkE088sbzvOHT/ffuubb+fDZOYHlzMfD0XAU9M53Kl5p5nPjVlQOXrCJaYdr2Obcsnj1zOfZ8X7viy7Jk9crfcFq+XfXK/3L7clrU5X+6Ty4/Hxnhi+iJ3Mj8vAcF0Xtdr9tnGD/zyh365HMdavs7lnG9vj9e7pqv2X25b1ub6nC+3bS8v98nl/K/N+Xq7xuuVCwimlV+g5tN7VX9wX9Xvq/l2WE/7fGdZzxk5EwLHCeTHueO6qL5RAcF0o/wO3iDgaakB9dQtBdOpxR2vW8ATU7fwCfoLphMgO8RJBTwxnZS752CCqcdVVwIEjhAQTEfgKV2lgI9yq7wsh52UYDrMy97rF/BRbv3X6NozjP+P6dgL6R3qWubWHfi/yBseTF40uYlXR+WKJ6abuGQ9x8wfxpznUS77Qd3eL/eP+XLbcjm35brL5tkrtx/6elkXy8vX2Svny+25X85zH/MzE4gnJhfxzC7a5nTzl3lznt/F9jvV9uvL9sv1MV/WLJcv25b75Dx7VV8v65bL2Xc5X27P5YebHfzy7lLqtMtH5UpcyN+N8dYYj8aIJ6hDGkawvTvGXze18Uuhpl6BuGZxjb42xg/GiL8uEFP+UF68ut1f4z6MX+L98xjvjZFmY9HUKBBvknE/vj3GLzfHOSRPYt/o8XnUfjxGrKiOd6LJmLbfuS/W+tohIIT2V2W1v9Wxe+YT6vdGo2qePK+LJ56Pxog/GpZPTGPx2imKY4oTiT8xYTqtQPjHD5w3g6vd48nJU/zVRjO3Zi7EU1M+yee6fY4T+0YmfRJfYsQU833/MXx5MO9Iz/lO/iWugTeFk7M74B4CyzfNuE/3zYjc9/6+QbTHudiFAAECcwQE0xxHXQgQmCggmCZiakWAwBwBwTTHURcCBCYKCKaJmFoRIDBHQDDNcdSFAIGJAoJpIqZWBAjMERBMcxx1IUBgooBgmoipFQECcwQE0xxHXQgQmCggmCZiakWAwBwBwTTHURcCBCYKCKaJmFoRIDBHQDDNcdSFAIGJAoJpIqZWBAjMERBMcxx1IUBgooBgmoipFQECcwQE0xxHXQgQmCggmCZiakWAwBwBwTTHURcCBCYKCKaJmFoRIDBHQDDNcdSFAIGJAoJpIqZWBAjMERBMcxx1IUBgooBgmoipFQECcwQE0xxHXQgQmCggmCZiakWAwBwBwTTHURcCBCYKCKaJmFoRIDBHQDDNcdSFAIGJAoJpIqZWBAjMERBMcxx1IUBgooBgmoipFQECcwQE0xxHXQgQmCggmCZiakWAwBwBwTTHURcCBCYKCKaJmFoRIDBHQDDNcdSFAIGJAoJpIqZWBAjMERBMcxx1IUBgooBgmoipFQECcwQE0xxHXQgQmCggmCZiakWAwBwBwTTHURcCBCYKCKaJmFoRIDBHQDDNcdSFAIGJAoJpIqZWBAjMERBMcxx1IUBgooBgmoipFQECcwQE0xxHXQgQmChwf0KvDLd7E3ppsb/As7Hr0/13v5V7xr1591Z+5zfzTUeePB7j6CyYEUyfbAwe3YzFrT5q/NBFQJleFggbwf2yS+eaJ5vmHx97kBnB9M44iYdjvDFGnJh3qIHQOEUQPRjj/TH+NoZwGghbU5q8PdZ/Z4wvx3BfbiFNfhn3ZeTJ/8b47ozecYNH0wiVmBvnYfCbca1iipAyvSiQb7i/GKvdz+djEE+4cb0+zQv44mU97FVe+MOq7F0RiHf9ePePJ9QvKg1uWU3+80LMZ9zrt4yv/O3GfXrUE+qMi5UnkPPt7yaCK7flcsxjivW57vmKHV92bc91yz7L0twe65bL+Xq5byxvn9/29nidx4rl7fNeHiOXt+fbPeJ1TMtjX6zZvS73zf1znjXmLwukUcyXy3ltoiKWY8rty20XW178utw/9835cs/tdfk651ftm9ti35zi/PL1vueatYccM2tynrU5z/Ux37Vuub28PCOY4uAJtetElttyOefX1V62Petzvn3c5frl8mX9sn5731y/q265767lXJfzXT2u6n/d/stay9cLXHYdluv3MV/un8s5X57F9rp8nfOr9s1t2/te9zrrtufbdbF917rtuuV+u/bftW5Xj4PX5X/qP7hQAQECBLoEBFOXrL4ECJQFBFOZTiEBAl0CgqlLVl8CBMoCgqlMp5AAgS4BwdQlqy8BAmUBwVSmU0iAQJeAYOqS1ZcAgbKAYCrT3Vhh2//UdmPfkQMT2BKI//M7/zREzrd28XJlAvHL1nHd4tcBTFcLpFHc2+7vq63WsDWuV/wtp6dxg7++OaNZv56yaWfWJPDapm/8Iq/paoH8ywtpdvXetq5F4PUIo39szubzMffRbi2X5vLziL8Q+PUxPtzskk8Fl1fcvi1p8q/xrcd9/cEYca/7GDwQVjzlE9On/weba0V5U6WJqgAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"69700f79-d1fb-472e-bd9b-a3a3b9a9eda0\": {\n    \"name\": \"Pone Biometrics OFFPAD Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaMAAAGjCAYAAACBlXr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAHTmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4wLWMwMDAgNzkuMTcxYzI3ZmFiLCAyMDIyLzA4LzE2LTIyOjM1OjQxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3YWY3MjAyNS0yZDJhLTZjNGEtOWYyZC0xMjFiMjFjODUwODciIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2MjZhNDA1ZS1iYTlkLTg1NDAtYTcxYi1kNGVjOWM3MTUxNDIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZjI0NDI5MDctZDViZS00MWVkLWI1YmEtZjllOWM3YzkyYjUzIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChXaW5kb3dzKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjItMTAtMDZUMTM6MTg6NTgrMDI6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2ZDhlZmNhLTMzNzItNjY0My1iMjhhLTU3Y2QzOGJkNzBhMiIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjkzMmZjNmE4LWYwMjctMTFlNC1iOTc0LWQ5MmNiZGU5ZmNlNiIvPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyYmYwNzYzNC01MTk3LTRlYjYtYmY3Yy1mOGZmOTZkYWJkMmQiIHN0RXZ0OndoZW49IjIwMjItMTEtMDNUMTE6NTc6MzMrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpmMjQ0MjkwNy1kNWJlLTQxZWQtYjViYS1mOWU5YzdjOTJiNTMiIHN0RXZ0OndoZW49IjIwMjItMTItMTRUMTE6MzE6MjErMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT54bXAuZGlkOjc5MDY4MzA0NzNCODEx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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaMAAAGjCAYAAACBlXr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAHTmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4wLWMwMDAgNzkuMTcxYzI3ZmFiLCAyMDIyLzA4LzE2LTIyOjM1OjQxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3YWY3MjAyNS0yZDJhLTZjNGEtOWYyZC0xMjFiMjFjODUwODciIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2MjZhNDA1ZS1iYTlkLTg1NDAtYTcxYi1kNGVjOWM3MTUxNDIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZjI0NDI5MDctZDViZS00MWVkLWI1YmEtZjllOWM3YzkyYjUzIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChXaW5kb3dzKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjItMTAtMDZUMTM6MTg6NTgrMDI6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2ZDhlZmNhLTMzNzItNjY0My1iMjhhLTU3Y2QzOGJkNzBhMiIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjkzMmZjNmE4LWYwMjctMTFlNC1iOTc0LWQ5MmNiZGU5ZmNlNiIvPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyYmYwNzYzNC01MTk3LTRlYjYtYmY3Yy1mOGZmOTZkYWJkMmQiIHN0RXZ0OndoZW49IjIwMjItMTEtMDNUMTE6NTc6MzMrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpmMjQ0MjkwNy1kNWJlLTQxZWQtYjViYS1mOWU5YzdjOTJiNTMiIHN0RXZ0OndoZW49IjIwMjItMTItMTRUMTE6MzE6MjErMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT54bXAuZGlkOjc5MDY4MzA0NzNCODEx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\"\n  },\n  \"89b19028-256b-4025-8872-255358d950e4\": {\n    \"name\": \"Sentry Enterprises CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"4e768f2c-5fab-48b3-b300-220eb487752b\": {\n    \"name\": \"Hideez Key 4 FIDO2 SDK\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAAG0OVFdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMjFDOUI2OTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxMjFDOUI2QTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjEyMUM5QjY3NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjEyMUM5QjY4NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+vr5XIgAAE/9JREFUeNpiDDl6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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAAG0OVFdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMjFDOUI2OTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxMjFDOUI2QTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjEyMUM5QjY3NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjEyMUM5QjY4NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+vr5XIgAAE/9JREFUeNpiDDl6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\"\n  },\n  \"47ab2fb4-66ac-4184-9ae1-86be814012d5\": {\n    \"name\": \"Security Key NFC by Yubico - Enterprise Edition\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\"\n  },\n  \"931327dd-c89b-406c-a81e-ed7058ef36c6\": {\n    \"name\": \"Swissbit iShield Key FIDO2\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANEAAADMCAIAAABiENH9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAACZvSURBVHhe7Z0HdBzV1YDXDTeasSk2EEwxYMCAAQOGn0DoECdACKYkQCChBEiAEEgghIQEHAihdxLAXbLaFq16772veu99p/eZ1X/fzEpa1gWDZ6WV9t3zHR0dg3Zn3/vmvvtm572xjOHAMbWBncMx1XEg51RVHXUTNc1t9oKKt+LzH43Muzm8dH1Ew6lRnSttgysc7mNiiWOc1DFOEjPboaCvocdX2YdOi+q4OLL+1vDSxyNz347Piy2oqGvtcBME2OL15ttiv84JgtDW0ZFU5no3rfwRW8l1YaVrtpYf/XXtnG1tlmi3JU6yJI9ZUjGhBPQ49HvU6NxtrcdsrT17e8X1u0setxV/kF6eWl7b2dUliqLXngPGvp1jGMbV1BqWVfKcNffG8NIzwhqODu+cv6fPEjlsiSEsNsbi4CyxvI6ACQ307nawqPfBgYjhwyIGjg7rOHN33S1hJX+25UflltW3tHMc53Vo/7EP5wiCKK1v/iy99KGoovU7ylfsbJoT1ocki5eR5vCLjbZYSfTGmFAD+h2ci+WQCeCDnZ0b1nvczsZLdpQ9GlP0ZWZZZVMrRVFek/YT/s5BhgPhPkgqvCuiaM3u+kXhPXOiRixWGgkO2Jlx4bBzoQlpsVLIATDBUMJKzY0aWRLeszas7t49BZ+lFVU0tBw4233DOZ7na5vbPksvuWtP4Sk7XfPC+y1xem6zQ0al9np7TMgDVoB28RJIsiC877Qd1fdFFnyVWdbY1gHzAa9Ve8WkczDvaOvohBruoajCM8PrF0T2I5dhIIcXhZfGgylmb9BQC9pBcS9A8luwp/ec8NrHYgqic8s6u7o1TfO69c2YdM5NkMllrudici/cVroovBvJCzUjJFJsG+bAgCHgiVOwxIlLwjo3bC950ZabUVG3v8Ju0jlXS/t7aeU3hZUu395giRzSpwssrtswBweJMhQ4s2fg2G11m8JLPsmoaGjr2OfVk0nn7Pnlj1qLz9hVNy+8D2kLNRz89H9pDGY/wMQCTSnI+WG9Z++q/Z29JKGocnB42KuXT0w691Zc3vVhJcvCO9BfGjUcTnKYgwfVdrp2Nmb57rabdxe9n5BX09Lu1csnJp17NCL3zG3lh0X0o0oOCbvXi2IwBwacAXPixIXhvWu/Lv1ddF5KdZNXL5+YdO7m8NKjv671VnIwY/V7OQzmYHAw4NyciKFjvqq5LbIsvLLDq5dPTDq3PqJ+/s4O/c/0bxp8XwiDOUjs+vAa416wo/3y6IbPqwe8evnEpHOnRneiL+9tE9807PVyGMy3YtOrOhgno0bPtnW963J79fKJSedW2gb16yOcfrkFO4f5XhjyxHIWp7g6dujNOsarl09MOrfC4bYke9BVFr9XwWC+K04BXFrldG+p5716+cSkc8fEkugeKewc5tBxiuDSCXHE6w0HvCZ8jJNCzoGhfn+PwXxXDOfiqde+zTk9z2HnMIeON8+RrzXs4+4S7BwmAGDnMFMNdg4z1WDnLNEhj1+DBJrQdc5o7qh9ERlKwOedYu1muXOGWPvEVzK//xRNzJkghpg7e4FPZ3xM/3YLKLPWOcMeQyl0QrsnmVAtCrX7AiuxyEYstRNHOohlDnJ5LHWskzreSa2Mo1bFUyfGUyeNc3LC7OGUBPLkBHJVHHVMLLnERs6bypvTZpVz41lqUjj9FziP51qRWwt1t46KJVc4SfBpdQJ1ZjK9LpW+KJ2+PIO+Kou+Npu5IYe9JY/dlM/eVsD+rIC9s5D9eSF7l87molnC3UXsfcXo56Y89rIMGhRcbCe9jebXpIFg9jhnpC5IYxGQzNDvMHAsspFwHp8UT56ZTK1Po6/Mom/IZUCme4q5h8v4Jyr552qEv9QKr9YLbzQK7zQLH7SIH7dKn7VJ/2uXvu6QtnVK27ukHV3STp1ds4Xd3VJkjxTWLcHnfaKS25jJLIvVnYMG9GvVQDAbnDNO0PGsNi+GWGKHTEadlkivT2OuzUGJ6pFy9oUa/vUG4cNWEWSK6JGdA0rqkJI7qpYQaiWl1dJaE6O1sFo7p3VwWhen9fBar6D16fTPLgZFzS17hiRPmVv9uFW8vYA7Po6yROmFh1/bBoIZ7NyEanqJBpXZkXby5HjqglT62hxmcxH3ZCX/Sp3wfou4rVO090sZQ0qxW6ml1VYWyTQieSjFw6tjsmds3ystQyAGBc+ebvmXJdyJ4JxR7Po1ciCY2c7puQ0S2xEOcnUiDWMEFCt/rBbebRHDu2VIY2WE2sRqPWCY7KEVD6d6RM0ja2OqZ8zj/WQhHZDCw7ukX2Dnvh1vekO5DQqRtckUVGmPlMPQKW7vlNKGlGpKhZGRVsAtrNaBopPTdnZK9xZzMIHFzh0QPb0ttKHZ/tVZzOPlHAygzn6lilS7eY2QYcT0QCbD8a0BlesO7NyB8A6mbhhMVzjJ9ek0TPi3NAjWXrmGUqE+w0ntuwZ27tvQnVtgI06MJ6/PYZ538WHdcgWh9gsosXkPFMd3Cezc/jEyXBSxxIautN1RwL7RKKQMyZ28pmDZDiGwc/tHd26pjTw3hX6wlPusVSx2q6OSBwt3iIGd2xdgmw5kuHWp9MNl3LYOyUWp3MFuw43jQIGd2xd6hltsJ85JoUC47Z1SA63i6s2swM59k/EMd5iVWJNM3V/KbdWFk7Bw5gV27pvok4b5VuLkBOpnhewnbWItznBmB3bum+jOHRtH3pjLvNkkwKSBw8KZHdi5cYxRFco4G3lROv0nF582rMAs1XsoOMwL7Nw4unALrMSpiaiMi+iRe4T9bLKN49ACOzeOPqoujyVvyGXebharKFXGOS4wgZ0bJ4qYZyXOS6X/UM0lD8rDEs5xgYoeXtvdJd0X0s7pldzcaGK5k9yUz/6vXWxjNQV/dR+wGBS1iG4J3bMZH7LOeb9UJS5ASY7PGJbZKfx6C95J86DbOQF4WwDGdFnzAtl29tHMaFs7pbuLuJWh6xx85igCjua2fPazNqmZmaKrI6LqoWQPDOL9gtbNo/UQrawG/dHIaPW0WgtQao1ONaVWkZNUzkzgyF36B0kcULY0Cjfnscc69fUQgF+PBIJgc25uDHFeCgVJLnkwsNdHQGdGGRsQwS21jFAzh5X4AdnaK+/plnd3yds65a86pC/apU/bxI9bxY9axA9axPebxfeaxXd13tGBKc5MBI4cPg58ir/WCfeVcOvT6SMdobnWEF0icR9uJ6/PZqCbGxgNRrQABQzZUClmDSu7u6V3W8SXa4Wnq7hHyrkHSzmYxG0u5O4s5G4rYDcVsLfmMbfkMTflMjfmMjfkMNfnMNdlI67V+dHMBC3jzUWf4opM5pwU+vg48jDbXt0ROILFOf2a3LwY9E3XAyWcvU8eCUySg4oNXrmUUHd1SS+5hLuL2auzmXVp9BlJaHU7lDXHx6F1/CucFHzko4FY8igHCWkAOMKOgLNi1rDUTi60oS8Y5/h1R0AJFudQMYHWpV6UTr9Ui77pCsQ1ORAOKrb0YeXfjeIvirkN6fRJ8eQRDhIGdEN6xMTmMUZN7UvErAM+FLT81AypEwSLc+jDE8c5qR/nsR+3Ss2s+cMqCDcketIGlb/XCTBKQlZD+3RAcxu2Gb8Yv4cOE5/arzsCShA5F02cnkj/poyL6ZUHRPOznFv25I6or9UL1+UwK2JJ1OLoRPdper9DwgSIoHAO+jsSijnyojTmZZeQM6JQZl+Wg5G6hlI/bJE25bMnxlFz9XecthM9xAke5xbZSJhSwRy+llJFGAjNC9UzBonT2is/XMatTqTmw9sZI4vfYWCmhul3btyAZbHk7QXsri65mzf5LhJGQRPVNxvEH2Yxi23e2hHntmkjSJybE02cGE89WMo5+xXC7KskfYIW1SP/uow/PZHSc+qUz9Qwvky/c3qSW2Alz0qmf1/FZ4+ogqlrujyesTpKfbtZvD6HRU+QgreDSs7vGDBTSZA4t8ROXpxO/6VWgEHQ1FpuDErD3BHlD9X8eak0lIzG2/kfA2YqmX7nIOtEE0c5yKuymH81Ci7K5JWrbtnj6Jfv12/XQZs1g3B4YJ1egsS5FU7yplzm/RahiTHTOUiZXby2rVPalMeC1pPXR/yOATOVTLNzqKJHP1fq9y990Sa2c2bOWSVtrI5WP2gRr85iFqIZKxYuCAgS506OJzcXsZCQugUznWPVMSgQtzSKl2bQaDt67FwwEATOoSsXqxMoKLnCu+U+U7/1IhVPzojycq1wYRqN3g5fJQkGgsS50xOph0q5qB550FTnRmVP2pDyxxr+3BQKvR12LhiYfucikAdnJFGPlHPWPnnIVOeGJU/ioPx0FXd28vjVYL8DwEw9QeLcmiTq0XLOZrZz8GpxA/KTldyZ2LngIXice6wc3R5srnMwUjv75ScquDXYueBh1uc5cO5J7FxQESRzCKjnjLs1zZ1DDIue+AH5d3hsDSqCxLnT9HlrZI/JdwiPSJ6UQfRl61qYt2LngoQgcW51AvXLEi6sW+4VzHSOkD1Zw8qLLv781PHrc77vjpkWgsS5k+OpzUXctk6pizfzewhG8RS71X/WCxvSGfw9RLAQJM6h71sL2C/axTZTd0QXVU8trb7bLF6VxaBlw+AcvpFp2gkC59DPFU60h6vp95WonrFOXtvaMX5fiXHzHE5100uQOHe0fv/cGwG4fw5Kutg++UH9cZHe5V7Yuellmp0D9Nyz1IbuE/5rnfn3CUvaWOGo+qJLuCiNWYLvEw4GgsS5BVby7GTqmWo+Z0QBS8yNVlb7b7t0RwF7AtpNkrBE4FQ3rUy/c9D9+rqvHySQD5dxCQOy6Quq3bInY1h+ycXD7BUtiQDncFU3jQSJc8DyWPLOQja8R+oTTV7fKmsemElE9kiPlHNnJdOL4U1RtjN2vhn3z8D3wDABYvqdA6CzI4mldrRR+idtYiOrSqbuIQyvJWroosn/OtDeuecmU/Behuhe5wwmzMPyBZTgcW6+lbw0g/5Hg1BMKGwANnWlFU8lqX7VIT1ZyV2dzZyeSB3npGDuMt84gCg953kxUuCBmfifTWJC/e/KxHli4NuwwUnQOOeGkg6mEU9V8vED8ojp8wg92zGKp5HR4gbkt5rFx8r5W/PYi9OY05B85JEOYqENPSMA7f5ndJ5f184IZoR2weMcNNmqOOrnhezXHVKnqau/fAPmJ0OSVkGq9j75wxbpLy7hsQpucxHaw/WH2cylGcyFacw5KdSaZGp1Itp588R4amUcdXwcCV4eq7PCDKB4BY7RWaZztANxlA7a01Pf1hPthmlDu2EuAWzEYhuxyEYstKJnPS6wEpCh58Xs6ySZyMTBaWFQOAfojQXNfWUm83qDAINg4LZMhxfmVM+g4GlitBK3mjIow/QCxtz3W8QtDcLLtcJz1fyTFdyvyzgo/u4u9u4t/JN89sd57C157M25iJty0SbD35sbc5nrgRzm2hzvHr9XZzE/zGKuyqSvzKQ3ZtKXZ9JQaVyaTl+SRl+URq9Poy9Io85Ppc5NodYmU2clUWckUacmUj9IoFbp+9GCwWDqIjuy0OvZxHiNnds30C5RcPqS0JS/reDAA0bxvnVAA8yG2nFIRPvz19FoA/XcETV1SHH2y9G9Uli3tL0L6ahvoC591Cp92Cp90AJ2Su+1iIfCu/r+5f9pEt9qEv/dKL7ZKP6rUdzSKML59s8G4dUG4e/1wit1wl9rBcjEL7qEP7mEF2r4P1bzz1bxv6+CU4J/vIL/TTn3EJwYpdzdRdwdaLtt9ppsZkM6ytM/SEQWLraR3q0Lgkq+IHIuGl2lg1Hmp/looWsnrymBGmD3ESCfqKH91EnZMyqBhZ5+QevhtS5ea9cfF9HCaJAXoRxs0Kk/NOpohItC1FBatU4VpVUSSgWhlBNKGaGUEkqJWyl2K4WjSsGokj+q5I0q2SNK5rCSPqTAiZE0KEPt60CnhwynB9QkH7WCteLz1fwDpdyNOcy6VBpSILq5YUI7v2afFoLFOQC0i0RlyoZ0+tU6AVp5alJdUIXHg9D0n8YvcDIYD+UBZP3EEAAVygN0hsBknFTQSTIgoNOjSX/WBRgZ3i1DKn22mr+zkIUx+uR4VBRO5rzpTXhB5BwAJUg0cUoC2oguvFvqMXVN/6wPaCxZ84CIoGA3r9VSWsaQsqtLghP43mKYodMwd0E7xE/MLXxbfioJOudgJmGHmQT9j3oeBpfAzSRCIXjV0yd4ikbVHZ0SlIMwAVqThHaL9ya86dIuuJzTM/989N0rdX8JC2UK1FWmfiURikHLHihJYcD9oEV8qJS7MI3+xq2EU29ecDlnNAF6tCF5RQb9Wr0AFTRj9lf+oRlgXi2tQsUC094rMpnlsRQaZw3zfLtgCggu5wwiiTlRaIXEPUXs1k6pPQDPJwnNgPIYGtPep/y5Rrgqa/whGVOf7YLROb0hINVdkEo/U8UnD8im72odsgET4V7BkzQgv+QS/i+LWeaYjtouGJ3TTztoi6NjyetymP80iZWkyuHZhEkB7TggaHH98nM1/IZ0+oiJ2s6vFwJHMDpnoLfC6gTqnmLuqw6pkdawdWYFTMs6Oc3ah3ZyOTeFWmDFec5AP/kWW8lzUujHKjiYw/aJHlzZmRWyNtbKaXAy/7yQPSmemg/aTdkIG7zO6SMsNMThdnJDBv18jZA0qPSL+MqJacGraMH5lgbx2mzG++SMqRlhg9c5AJzTrxIviyWh4H25TkgdUoYDcGtdyMaw5EkYUH5fya9NphfE6NpNQaoLaucAPdXBfGKFk7w2h3m1ns8YkofMvXU9hEPxjDWz2udt0o/z2BVOfRuhKdAu2J0DjIZATxQmr82m/1bHJw8qfYLH3GWwIRus6skYVp6t4tel0gttxiM0AryT0AxwDtCdmxtDHOtEz9t8ySXAVB9mXpLJa/5DMeDMbWS0T9qkn+azx8VRqKkjsXPAeKqDUxAG2SsyGDgvw7slF6XSgXhuf4iFW/LA0PFMFb82hda318DOGUBb6OZBtjvKTq5Pox4uZT9rE4tG1SF8DeXQAoaLOkp7r1m8JptdYqzCNFrbrwvMYsY4Z6BrByfiYhtxZhJ1RyHzz3rB2qu4KA3MwzcDfM/wjI2Inuhe+d5ibmU8Nc8QDjs3CbSFfmvnQitxYjx5RSbzSBn/QYsEowNMwQgZzy2+T4jaWI7+xFGYSSwO9E5CM885wDgL0RDgXmonz0qib81jn60WPm0T4/qVckJt57RhEd0xizPfQQa0Uy2t/qdJvC4n8NeHZ6RzgOEc0g4SHlp2em4KfWMu85syDkbbrR1S4oBSSqgtrDYgeCjFI6joWhS6rIct3E9089rOLum+YvRVGGrYwM0kZqpzExjmRaGlxUc5yDMSqf/LZDYXsk9X8a81iJ+2SWHdsrNfTh9C66ZKCaWKVGoptHyrlUPrC6GhewWtX/AMiJ5B0QPZcURC6wlgKgfDNEDqUDq0vubFF+b7AjkY4FQEr6I1NTC6SRpaZaPqS2+mPuCDJw0q0G5nJ9PoBifs3H6ZSHhANFrgDuatiiPPSqY2ZDDX5bJ3FHAPlHJPVPDP1/Cv1AlbGoS3m8QPW8Uv2tHiPDiz9/RIUD7b+uTYfjmhX04akFMG0WK+zGElWyd3WMkfUYGCUUShG1H0fSkm1BJChRxcRqhQBlQSahWpuigVhrYGGiVmOBN6eG1Q1NyyxqqeKbsWxCpjcGD/bBA2ZNCB/cp/xjvny4R8envNs6K9nlbEUifHUzDJPT+NujSDviqLvj6HuTWPua2AvauQva8YGfnrMu7RcvCS+10l90wV91w1/3w1/2cX/6KLf8nFv+ziX6kV/gbUIV6tn+Qf3x3oVGjr1xuEfzUIbzQKbzYKbzWJ7zSjhdYftYqft0tftkvbO6WIHsnWDxMjGdIzJOZeXoPsGND8B1kW0v/HbRKUdIvt2LmDZCLnod060AYo6F+i0SU9yH+LbcQRdrQnCNTIUP8d70Tp8KQEtP3C6kTqtERqTRIF2XFtMnVOCnVeCrUuFe3VcEEqdWEqtT6NuQhIR1ysc8n3BbIvcGkGc1kGc3kmszGTgak31AM/zGZ+lMPckMvcnMv8JJ+5E86HEhbOBEjPUNpH9sglbnVA1AI3K9JvIdZ2dcu3F7DLYBphNGYgtJtVzvmhC+e18CAx/sRAfxEoEyexIuYeGpB9ARi8fIFTYqGNWGQnljqIwx1QHhDLnST0yqmJ1Pkp9HXZzEOl3L8aRShMmxgtcLdMQ/3q6JcfKGFPjAvkPeuz2TlgQiBfsSINjM2LDpKJvzIV3+PxHh6kZwN02PNj0F2ry2ORfJAIYejf0SXV0SoXmHQHUxkoZKHGOCOJ8t7aBPg16aEzy537fkyYOl14/dMZ1xFq03Up1MNl3K4uqYVRAzG3ULSxglHlhRr0qCpw3XsAfo1z6GDngot9ygfaod2PiSVWYl0KDbOc+H4ZajvV7NsI4eXKSRXmSZelM1D7YudCG8O/CHSn/jVZzL8aA7XavIZSYTZ9tb4METsX8oB2e5AEqxOoB0q4Pd1Sr6nP4zOigUY3mNyYM77iGlKs32EcOti5mYRe2x2lP6dqS6NQTZr8zCCIFkb7tFX8SR5zAvQ4dg6DJIhCk1mYVz5ZycEc0/TrJh2c9mW7dGcBc2Lg7hnGzs0kjKouGi0N2VzE6o/1Nvn5Ld28tr1TuqeIPTlw3/Rj52YY+ni31E7elMt80S61sKps6uy1V9DCuuVflnCnJIw7B6L7HcMhgp2bYYABkWiz76symbebxCpS5U0dXvsELaJHfrCUg5kKdg6jg5xzz40hNqTR/6gTCkcV2tQrJv2iFtUrP1TGnWo4F4Gdw+jOzYkhLkilX3IJWcMKYeo3ElAgRvfKD5dxpyVi5zAGunPw89wU6rlqPnVQHjV1c75J55L0Zf3YOcyEc2uTqWeq+CT0bDRTx1ZhfGw1nMP1HGbCg7OTqd9X8on98rDZzkX2yL+COQSMrdg5DMLwIIZYm0I9XcUnmp3nYN66p1t+AF8rwUwy7hzUc3+o5lPMrud6eW1Xl3xfMfcD7BzGi+4czFvPT6P/7BIyzJ63dnHatk5pc1EgVxxi52YYunNzY4iL0+lX6oS8UcXcXYLaWe2/7dLthexK/H0rxgtyjphvJTdmMW80ieWkyfepNzPaR63irfnMcdDjyLm9DuDQwc7NMHTnFtnJa3OZj1vFBkaVTL2fqY7W3m4Sr8tlluP75zAIEA480DdYhuFvZ5fUw2tmKucZqyLV1xuEK7OYyWeC+R3DoYOdm0nozs2JJk5OIB8qY+MHZNLUYg70LXGrf3EJF6cxSwO3OxN2biahS7DYRl6YCpNWvtCtKKYOrJK+I9gzVfw5yfRCY/sI7FxIo1dyIAFU95vy2E9axWbW5P2UaWUscVD5TRm/OoGaPz6O+x/GoYOdmxkYBkSgfTDOTaGfruJgYDX3URmQMIdET1SvvLmYA63nGMLB+/odyaGDnQteoL8NoO8hw+mPGD3OSd1RwP63XWygNcHUgVX1jLVz2pcd0i357OSD57BzoYVhmyFcBPplmQM9D+jVeiFvBN2qae5KCDC4mlLfbhbhLQ6z6W8aCOGAoHNu4szGGEQT86KJxVZiZTx1TTYDU4eEAblPMH9lK0yBs0eUl1zC+Wn0HJhAhIpzhm2RvpvTzAoi9sL3P038DvioBiywov3LToonL0qjf17EQiclDsjdvCabrxx6qKu1V36kXL9bE/oCnPPtGhMJKufmWgnI6ovsxBI7OauwTbLUruMgD3eQUDYd6SCPiiWXxZLL48hj48gT4qkT49F+eGuSqHNTqEvS6R9lM/cUsy+4eKi0CtzKINqmxNsp5kYLo33WJv20gD0uTt98LhScg3x+pIM4JZG6II2+LJO+QufyWcHGDMQVmcyVOsYOhzBQXmdscpjHbMpnbi9k7ypif1HC/aqUe7yCf7oK7fK5pUH4qFUM75Eyh5VGViMDtuMheFxGqH+vEy7PYA53GKONfweZRrA4p2/FekoiWrb5RCX3ch1v7IQKrTALeFUHbexajzZ2BZPeaBT+3SS80yy+3yp+3Cp+3iZCGtvZJUX0yLZeOWFAyRhWikYVF4WeOzAkeTgVaRG4QFfmBpRHyvjTE+nDAnc12CAonNPLOKhdLkyj4fze1S1BMVvsRhSOKgUzn0K3UoRQi93eDazLCbWCRLtX14zvXt3MaqBXF6/B/GBIRFu2c6pHmZKHhkL2hPf9ulPalM8eHRvIHTYNgsU5fZ0wzNL/3SRCkoc5lKTvXQ+IswvjQ8EkwAD62wDSmKYDlk2FaD5BKx44JV6tFy7JoNGz+QMqHBBUzkGJA+VLCxuAWRmO/UcvWrsvQR0JxbQx5vh3kLkElXM/zGLeaxHraFU29Qo7jgMEDN/lpAIl5tVZzNGBu3/Jl6By7qosBspqKHHM3YMDx/4CWnlE0qx9KMmtTqAWQF9g53AENFgVkpz6ZqNwVRa9dOI5JIBfB5kLdi6Uo5NDKwu9O3/pvYCdwxHAIGUtY0h5oUa4JJ05PHC7pO8Ndi40Q1DRXSQwY7sxlznWSaLH8BsDq1/XBALsXAiGonmaGG17l3R/CXdqIjUvZvzZaH79EiCwc6EWigdtGhzVI/+2gj8vldYfYag759cpgQM7F1KhjY318J64fvm5an5DBsxV9VtIDPw6JXBg50InJM3TyWuOfhnmDVdk0gF80s2Bwc6FSIjaWAurRvdKz1bzGzOZZbHj8wbsHHbO9IACzi15Kgh1W6f0ZAV3STo9+R0XtDzg1x2BBjs3u0PW0IXftCHlP03iPcXsulTqCIe3wafBNgPs3KwMaD9G8cD8tMSt7uqS/+QSbsplTkmgFtqmL71NgJ2bfSGoyLZitxreJb1aL9xTzF2cTh8XR86Hpp4YUn3bf4oJKueuzmI+aEH7W2lTcn/s7Ag4PSUNfVtPyJ5eQaun1ZwRZXeXBD36QAm3MZNeFU+i9AaNDLb5tfy0EFTOXZPFfNgiNjEmb8Mx+0LT97OB0XNU8vTwWiOjlRBq8qC8u1t6u1l4robfXMRemcmclkgd6dDvNZ9YxejX8tNCUDm3MYN5rV6A07RP0NyyBxiWZjUiYkhnUGdAQPTp9Argk6eL98AkoJ3TWlmtiUVpzEWp5SRaZpE+JDv75bBu6dM2cUuj8Ew1f28xe002vTaFPs4Jpdv49d5pH0z9CArnAP2xpOekUL8u4z5oFaN65dh+hK1vNmPtRcT0ytG9clQPIqJb2tMtQbra1SXt6JS2dkpfdUj/bRc/axM/ahHfaxFh+vl6g/hKHf98Df9UJfdQGXdXEXtzHntlFnNeKg2zhGWxpHfhFsxMjfRmmOfX4NNIsDgXTcyLIeA4Ls9k7ixiHy7jHi1HgIKzGPiYBqDOr0q5B0u5+0vYX5awkK7uLmLvKmR/VsDeVsD+JJ+9JY+9MYe9Npu9KpO5LINZn0avTaZh6FyVQEHXHO5AFdtcvRm9BKFqEwSLczHEnBhisZ04No48JZE6I4k6MxmxJmk2Ax/T4HQgkQKHTk1Ei/ghXf0ggTpJX9O/Mo6C7jnOSa7Ql/sf5SAPt5OLbGhYgBbz6mWkNC/BN5j6ETzOGUA7AnDKhhTGp0ZEfwNvrtonIJaB378De7VqcBFczkF7obNWP1/99pIJafRMNiHZBDNFMj+CLc9NtiPmwPi12wwi6JzDzHqwc5ipBjuHmWqwc5ip5qCdo7BzGHMwnIunXmsQvXr5hI9zsaQlZcwSy/v/PQbzXQHnUiDPEa8f2LkVDrcl2YOdw5gAjJZJnlXO0S31vFcvn5h0bqV1AOnp4CxWymIl/V8FgzkYwBzwJ5YD7VY7Bt+spb16+cSkc6dFdViiRi02BgF/5vdaGMzBYKMsNtpipy2RI2utne/WjHr18olJ5y7aUzd/Rzv6M0h18Ge+L4TBHCRgm4O1RLsP29G2Mar+i+p+r14+MencreEly752zYkatsRJFjvj/1oYzMHgYCxx4pyIwRVf19wRWRpR1eHVyycmnfttZO7Z28oWRvRb4iWkKi7pMN8VcAbMiRMXhfeet7X0mZi8tOpmr14+MencO/F5N4aVHBPWgZIc/CUaXrF2mIMGhANndHNW7G79cVjRR4n5rpZ2r14+Memcs6Dyt7aSs3bXzQ/v9QqLZxKYg8cQLoZcsLvn3N2uZxwlySVVQ8MjXr18YtK5utaOD9PLbw0rWbGtzhIxaImX0WQCpzrMQUGiK7tQlYX3H7+t9rbw4i+yKps7uiRJ8urlE5POESSZVl77J2vuJTtKl4R3w6iMXgVfq8N8K2AIeOIULHHC4WEdG3eW/NWem1NVT9P7uDgHMemcqqodnV2ROaWPxhSuDas9LKIPZUsnmKdfOsHmYfbGqOH0K8AWG7VwT8+6sJqnbIX2/PKenl7PfpbNTzoHIQhCQ2v7l5ll90YUnLajekF4H7pugi6dQGGIazvMXoAV4AYY4hQXhveu2V75YFTB9uyylo4uUdzHN61GfMM5CJZlKxpaPk0pui+ycO3uuqXh3XMjh1HmhNoOQPJBzgP/cNoLSYxh1JguGEpYqXlRw4eHda0Lq30wsvDLjOKa5jZIXl6f9hX+zkFQFFXZ2PpVZvmj0YUbdpQev7NxblgvuoACFSIYDW+Dh9qQBQ2mDBpMjQHQxszb3b1yR8PGnaVPWgu3Z5e7WtoZhvGatJ/Yh3MQHMc1tLRH55a9aM/btKf07LC65WHth4X3WvYMWqLd3neFIRyqPUwIIegZhwEH5kQMwmC6fHfbObtrb9sDk4Y8qOGa2zsPnOGM2LdzEDAed3V3p1fWfZxZ8ZSj5Oaw4rVbS5d/7Zq7tQXdCgBHkDyG7vHEhA7Q49DvkcPzt7Ucu9V13rayW8OKn3aUfJZZkVVV39PTs88rI3vHfp2D0DSNJMn61vb4oor3Ewueis77aUTZZVENZ1u7TnEMrnK6T3ASJ+hr0DGzHQr6Gnp8tWNwrbVzY1T97RGlT8fkf5iUn1Rc2dTeCfUY2OL15tviQM4ZIcvy4OBgdVNrSnVzWGX759UD77rcb9YxW+r514AG9ChwzOynnoce/3cd827N6BfV/Xuq2tNqml3NrUNDQ4qieF05uPh253DgMDewczimNsbG/h+9P7+KfKO+RgAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"8d1b1fcb-3c76-49a9-9129-5515b346aa02\": {\n    \"name\": \"IDEMIA ID-ONE Card\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"454e5346-4944-4ffd-6c93-8e9267193e9a\": {\n    \"name\": \"Ensurity ThinC\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"e1a96183-5016-4f24-b55b-e3ae23614cc6\": {\n    \"name\": \"ATKey.Pro CTAP2.0\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAAA9CAIAAADAuAeYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAABGuSURBVHhe7ZwJfBPV9sczS/Y03Rco3XcKBVwRBHkiT58LqxvCE3AtoIICBQTZ2gItUigtVGihKPoXAR+yyPLhARZZ1EdVoPoQpKW0BVq6Zc9klvxPMrfQliZNl7QPP/l+LnTmnDuTyfzuvefcm0kws9kscHEvg6O/Lu5ZUC/8z4BnNL8WYYSIt3Y7HGsIeH5M/M4stO/CNkjCswPHan/5HRf/r0jI6gz+45/t/fkatO/CNmggxXhwHLbMNGM20d1TaEaAWy4DwzG4Ev7aXNgH9cLCR8ZBL8TEIjPHyWLCcYLo+jQHpDPTtO7iFUxIcnqD38vP9t6WgXwubNNUQpFQQDODq88Schnv7mKoylunAu4nlZ4uCR2neUYKirJaPdrpcjidAW25cJgWJxVdPYQ2ohtf+l7FNS+85+lMCVmDkTOZOBPF0TSHbC6cTqdJqP/vle9k0af8Hjrp++BJZT+mph45XDiZTpPwYuICAhebWVbAmVmjoWSxa1beRXSOhPristoTx3GFDCMIjMAJhdv1TdtpjRa5XTiTzpHw8rSFBOmBYRirN3IUIyAwAU2XLs5EbhfOpBMkNJTdqD58hJBKYELpN/455cN9zRRNKOTlG75g9K55ntPpBAkvTV9MkAoBJmBYTVTWorDUObSxDoZTjjJeS3Z91OB0OiQhzMMN16uq9x3CZVJOb/AZMUKodPMYfL8iKp6jaFIuL1+/jaNMqLYL59AhCTGB4MrMFIIQwzbNqGJyV/D2yDULGGM9dETIaErTN/JGF06iQxJSlbeqdu63dEGD0XvIMGlIIG/3eeZvssgYmOALZfKyNfkczfB2F86gQxJeSUrDcEIAiSitjtmYiqxWIlfOZQxqgZBg62rL1my22lzrn06h/RJS1bVVn+8l5FLOSHkMHCSPi0QOK77jnpKFRppNDC5TlGVsZs2cddx10fm0X8KShRlmM2vpgib17SjYmLC0JMagwUjCVHmrYt1nyOqis2mnhHS96mb+LkIuMzOMcsADsqhQqqoaQuPtYrpV6/X4I9KgYAHLEVJZ+apc1zDqJNopYcmSdWYTDTknRpLG4rKTnv1/CB7yQ8jQ2+VM0OAzIY8yKq2AwHEhaaiouL7pS3Swi06lPRIyWv3N3O3WhzMsz0yZIc6RJCYSNi8EASkMVIBapFR+bcUn6HgXnUrzZ2egbz1SekLk78u7W+TSe0uvZX1Ckm5oH4HhMgnIBVsgKmegmqWgNFPXOyczMPEVtN8ShuLSMxFD7n52JjdvS0HBCYlYrKeopYsWRkU1SZ2akZyS+uefxUJSCNdSr6p/8IEH5ibNrqmpfStxuqe7u9FkHDjw4XemTd29Z++Or3bI5Qo7mbKJNvVLSJg1a2ZxcfGsOfO8Pb04M0eQRO7GHFTDNnq94d0ZM+FO4BheW1+/MSfb19feXW03JPrrMGaW5erUPV56wdrJGoC+JiKrvtwvEAlBQFws9h33pOWJwkZ3hzPRhj+uoJ02cuHChf3fHpDL5VqdbuZ77yBrSyTNnb8pb7NcJocrUqnU8fFxu3ZsBztFGffs3Rvg76/T6iRiCVj+vHxl7/4Dnh4eZtsaGg1GygRtURAeHn6hqEij1pAkWa9SjRk9+ul/PMnXscXWrZ/u3Pm1m9LNaKDuG9DfSfoBbZYQlIvdthrtNOVG/g5S5G5mWDLQIy5/FbJ2BiKxWCqXQWEFHMRWZL2LufPm5+bn+/j6gn5wo/sPSPj+u2O8C7qCVGo5A2c2w9nAIhTC6G6x2JEQw3GRxKI3kJaaMuXtRH8Pd5wkl6eltSohtCRPH2+RUKjRaFNSliCrE2hbLKQp09Xl60tXbLianFX+yd3pScO9YFm0YQWspatyr6Zml8KxGVts3rCOMW/+wo15+d5e3tb+p4qLir6tX4vo9LqayltVllJtp6jrVXz9cc+PVcjkLMeKxaLffv+9sLCQt7fI9q92lJVXCIVCiqL6D+j38EMPIYcTaJuEFRn5lxYsvvLhqouL5pEyS1t2BAiPdFXNHws/urJg1aVZc27tOYIcnceChR/lbMr18bHqp1ZHhoefKDiKfDaY9f7M2pqbZSWXym2XqhulX2zbig6AV5k3R1WngpdQSGXJKSuRtSXWZa9XKOTwxuvqVR8mzUFW59AGCSEKlmfkSWQBhETqHv5gwKtjkcMBwlLel7gFEQo3kcjvqvWj4E7si/MXfJSVs9HX1wdurlqtjouOPn2yAPlsI5FIPD09le7udoqHh4dCoUAHCATTp0/DMYzjOJFEeurMqeLiEuRoysFDhy/+cVkoEtE0HR0R8dRTrQy5HaQNEpZnfWaqrhIICcaoDkttU8syE2Jx0MwprFaNSUTac+dqDp3orNW2JUuTczZu8rPGP7VaA8lqwfF/I1+LYB1qPW++8ZpGq8NxTCgUp6V/jKxNWbs2SyaXwfVAPJ71wQxkdRoOS8iZyz7OJaQKs4mRBocFvPwMsjuERa+g2a8TCqWA4wiRvLMejlqyNGVt9nofH0v/02g08bGxJ+3GPwtm69W0l6SkOSajEWZikBvtP3CgtrYGORo4feaHs7/+AvMfhmEC/QNeGf8ycjgNRyUsz/vSWFGOCUnaoA5b0p6WJVQqA6e+wmo1mESs+qmw9vgZ5Ggvy9PSIeT4eFviH6T70VFRR44cRD7bgH4dkdDDXTl2zCiY8+E4TjPsuqwNyNHA2rWZoB8/JCQmvoWszsQhCSG/LFu50dIFaUYaGNRjyvPI0UaCkt7GYSoNHVEo4yNiO8AJyzUvX5m+Kn21l7cXTEmh//WOiz125JCd+cZtYBTlB9Kqqqpfz50v+u13O+X8+aKSq80D3sL583RaLXRESFi2/d+XEPCQQyAoKvr9u+9PSqVSlmXdPZSvTZmMHM7EIQmrtn6tLymB4Z81aEI+nIasbUfs49VzygssxBKpuP770/WnLXl5myITZBNKN7fs9TnpqzO8fX1APxNFxcfFHT64HybdqJJj5OZtGTDggUFDhw0aYrPcP3DQjPdnowMaCI8If2zoECNF4QShUqnzNm9BDoEgMysLjPyo/uqECfIu+YKYQ822dHmOUCI3M4w4oGfPt+2tkLVK0PxEHCbLHIeT0pJFa5HVYWRSacrytOQVK72t46fAbGYoU+7GHJiBoRqt0jCMKuQKH39/fz8/+GerBPj7QVaKDmjEgg/nqVUqzCyQK2Sb8pCEpdeuHThwSC6TQcoqkYindckoCrQuYeX2/frLlwUiEavXBs15gx/H2ge0BklPf/+JY1itHpdJ6o6eUJ0tcjwyWTTD8CPHjrkpFNAdeQtGEnOS5vMVHKKh1xuNhrq6OlV9fX1dnZ2i17XwQPPDDz2Y0LcPRZuEpLC8vGL3N9+AEcYGmmUgRmp1urGjR/n5+fGVnU3ry9w/9n3K+Oc1DOKMTDqw7CRpXZ1qkWNYCKn0gHgp7uU/8JLNzNBQWvFj9HBcJOSMlOcTg/sdzEcO28vcs5PmffHl9sZTNJPJRJtoyN1Bxprq6pRlS6ZPTUS+lrh542ZUXN+AHv56rW7UqJEbsjNPnjp17Ph3MDtENVqCppnIiPCXXnwB7Tdiz779r05+3c/P12g0xsXE7Nvzr9j4BMtXzDFMr9OdPHEsIjwCVXUyrcSP6/m76otOkQIvRqCOmZ9sRz/ALGAt39NnoDRZYGuGNCTQ78Wnb37+L0Iqu3XosOb8RbeEWORzDK1W2yc+ftjQIZmZ2UovD08vr2Upy0cMHx4dHYVq2OZ26H108GAoaKftjHru2eBegRqdXiwWXy4uHj9xEs0wkMjAtT054gk7+jEMu/2rrwICAmBI0Wg1JpoOCw3pl9BPJHI4FjTF3qgI7xb6ZUxKWlT6gtjlK3rOfB05bCD08hX6+wgDfElfL2SyQcjiGeLAQKG/r8SvV1nGnXTAEeAeBQf12v/N1xCQ+t3Xz6DXwwAhEgqnvN5Fsec2774zXaW2rLcROFb488+gHwxpDM3MnPEuqtESJGn5HYORY55/dvSYc+fOUxQ1aswLUbG9YUhANdoKnA44O3Dsd+LYAre+8D91s4o3QljmNxyhWVXHj4RXuV1Zf+XqUUFQgTLhOBn128T3kdVsnjVnbkCvkMjY+KCwyEGPPgZvm7eXlpUFBoeFRcZExMZ7+/VY8NFi3n43N67fULj7wBl69AqdOv09ZO0Y0IFCw6PComIjY3tHxMTDyQNDwkeNGYfcdomK66P08r106RJsnzx1WqrwCI+MNRgsiwZtxV4vtKQPDtOsapuSFAcrw+VC/FuXmSESod/HCe7VKzV5aX29Cnwenp7Z2Rt++s9Z3tUFCEnytSmTNCoNbFuzYzNo8MFMx9c9MMpo+TAyNjbGTeEGg2p5RTnvqKyqgv9rqmsqypEFKDz787Lk1G2ffwF5ADJZaUnC2+Gi62n1pTEzhjW55kmv/nPE8L/pNFpoCR5enhP+OQk5bNGxNdJmvPfuOxKZGMYR2IY727dvn6FDh/Au+6BrsLZevV5nNBkJgoQZTlb2+lDo1PH9Pv1sG/xNGPAQTDGhDnTuF1+Z8NLLL3762RdePgGNW2oLElp+tqe7aO2l4Z3DyIt2Gsjfslkmk9E0DbNDlUrTSlDs2BppM9zd3UNDQlnWEgogSM98dzpytAZcA8jHT2cXLlisrq2bNHGCm5sbxNeQ4F6EULh9567nnntu0KCHwThn3od7v9m7Oj0tJipqS94nQrF45Og7HxM1l9AMN9Fu2ulUMMsI2eY7LJNJczZkq1QquI/u7sodu3btP2BzsdRy79BmJ3D06PFz5y+AEtCAIsMjRo8aiRwOIJfLZ8+bHx0bf/HS5d27v165Ej0Ob2mOFJW1ZvVn+Xn79uxmaPrbAweU3l49A3uCNzg42MfbS6XWnDmDFpmbTipgkCLIH8MfE9zV0rsCGOLg9d2U/DNUbeLvI4ZPGP/Sjl27QULI1ye/9sa1kssyaQvrW5Z+bN1Yty47dWU61LfutYyRMj4+bNjWLXlo/y5WpKd7KJVmgaULLl20EFkdQ6fVZa/JCAkNQfsNQEOE9w9hld/V6Q0URYMFJqC8BaYxkARTDRGxSS+0JBY4xplojmG7odCs5QF+jGhfN8lelxkY4A/JKg5zDLF47LhWPuVhOY6GGQDL2ingpps+RNKYwsKff/zprEgqgXo9/QNenTgROVri0OHDGzbc+ZIXNFNoSTp9C7/SxLfg20keNLIe8L5MpqtXr/IWPajLsv0T+vO7SEKYj1uUo0yW37Jj2O4rcBkmuAyOsVwGf20AwzCQLJggiwev7R+Hy9+SB00bWivkiscLCrLX33lUEJq2CQ62nMMEZ7NYODPrAHyq0iIr0lYplW5wp7V63eTJk+wsPUIfhSY1fXpiQcEJZNGooYlUVlbyu43R6XQmFhrXna+DLVu8iMDwzMxs2D59+oeSPy/PTZrt4enOe9EC24WxibqiyzCR562OA2/A5h1tzWsHzkD5jBwetQYNTanLV36zd59UKoHhZfOmjQkJfXj73axavWbnrq8lUgm8r5qa2u+PHfX2sawzVFZVPv7EP7y9vYwGw99HjEhJXrJly9bsnE8UbncW7e4G+vSgRx5Z83E62m9EcXHJfQ8O9PH1AY2hw5wvPCtXyJGvJd6b8UHRb7/t27tbr9O++ea0G7cqhYQQJ7DRI0d+8P6decjSZckHDh3GCcLT3X3a1MRnn3mat//yy6/LV6ykGAYXYONffrHxmp9FQhCxodf+1YD7C+Mq2ulU3nhr6rcHDyoUCrVa/cZrk1OTlyFHl2OV0Npd2of9Yzty5v9lbt2qjo1PgGkoDNAmiir86UyXfS5xN5YW2pG7bP/Yv6R+wKqMNaSQxDEM8hEY67pRPwDFQheOYzAawyOiZdZPviD1OH3ieHh4OO/qFpwSJ/7awIQSkkkIsaDlsKFDulc/wNUL20yv0AiRSAQSqupVRw7t699/AHJ0E65e2DbSV62uKC2rq62/XnGjT5/4btcPcPXCtnHu3HmaoaELMgwbFhrivK+cOY5Lwnse10B6jyMQ/D/exLg8R/4sQAAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"9ff4cc65-6154-4fff-ba09-9e2af7882ad2\": {\n    \"name\": \"Security Key NFC by Yubico - Enterprise Edition (Enterprise Profile)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"9d3df6ba-282f-11ed-a261-0242ac120002\": {\n    \"name\": \"Arculus FIDO2/U2F Key Card\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoCAYAAABNo9TkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAD6KADAAQAAAABAAAD6AAAAADrEeKkAAAACXBIWXMAAAsTAAALEwEAmpwYAAACzGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cl9EK38AAEAASURBVHgB7N1/jGVZQh/2e+6r7pnp39VdPT1dVd0zuwwLw9iE0PxY2yRuSIRDLLBj5MgEQgw4/iGwHAKJI5wfsmXFimUlVmJHSpRETkikSLEi5a9EimNGOJEcdoddkNdr0AJDdjzs7A4sC7sz01317sk5577qqf5dVe/X/fF5UF2v3rv33HM+p7aqvnPOPaeqPAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwIoEwoqu4zIECBAgQIBAvwXy3wz1rAkxfW763Ry1J0CAAAECBAgQIECAAAEC/RPwH/T712dqTIAAAQI9FPALt4edpsoECBAgQGCFAnnUvHn+xo2vmjbNX6pCeCb98fDL77z55l9eYR1cigABAgQIjEJgYxSt1EgCBAgQIEDgpAIloO+H6YfryeSHQghV08RPpcIE9JOKOo8AAQIECDxGQEB/DIyXCRAgQIAAgQ8E6jjZirGp8s3nIdS//cE7nhEgQIAAAQKLEjhY7GVR5SmHAAECBAgQGKBAUzXX8+h5SuepddF/4B9gH2sSAQIECKxfQEBffx+oAQECBAgQ6LxAHcLFNpx3vqoqSIAAAQIEeisgoPe261ScAAECBAisTiDNbr9YxTzB3YMAAQIECBBYloCAvixZ5RIgQIAAgWEIlP3OQ4jXhtEcrSBAgAABAt0VENC72zdqRoAAAQIEuiBQhs3T4PkLXaiMOhAgQIAAgSELCOhD7l1tI0CAAAEC8wvkgJ5uQQ/nywru85enBAIECBAgQOAxAgL6Y2C8TIAAAQIECFR5yfZqd3f3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gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCLRX4H8D7duTS/D4+v0AAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoCAYAAABNo9TkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAD6KADAAQAAAABAAAD6AAAAADrEeKkAAAACXBIWXMAAAsTAAALEwEAmpwYAAACzGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cl9EK38AAEAASURBVHgB7N1/jGVZQh/2e+6r7pnp39VdPT1dVd0zuwwLw9iE0PxY2yRuSIRDLLBj5MgEQgw4/iGwHAKJI5wfsmXFimUlVmJHSpRETkikSLEi5a9EimNGOJEcdoddkNdr0AJDdjzs7A4sC7sz01317sk5577qqf5dVe/X/fF5UF2v3rv33HM+p7aqvnPOPaeqPAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwIoEwoqu4zIECBAgQIBAvwXy3wz1rAkxfW763Ry1J0CAAAECBAgQIECAAAEC/RPwH/T712dqTIAAAQI9FPALt4edpsoECBAgQGCFAnnUvHn+xo2vmjbNX6pCeCb98fDL77z55l9eYR1cigABAgQIjEJgYxSt1EgCBAgQIEDgpAIloO+H6YfryeSHQghV08RPpcIE9JOKOo8AAQIECDxGQEB/DIyXCRAgQIAAgQ8E6jjZirGp8s3nIdS//cE7nhEgQIAAAQKLEjhY7GVR5SmHAAECBAgQGKBAUzXX8+h5SuepddF/4B9gH2sSAQIECKxfQEBffx+oAQECBAgQ6LxAHcLFNpx3vqoqSIAAAQIEeisgoPe261ScAAECBAisTiDNbr9YxTzB3YMAAQIECBBYloCAvixZ5RIgQIAAgWEIlP3OQ4jXhtEcrSBAgAABAt0VENC72zdqRoAAAQIEuiBQhs3T4PkLXaiMOhAgQIAAgSELCOhD7l1tI0CAAAEC8wvkgJ5uQQ/nywru85enBAIECBAgQOAxAgL6Y2C8TIAAAQIECFR5yfZqd3f3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gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCLRX4H8D7duTS/D4+v0AAAAASUVORK5CYII=\"\n  },\n  \"fbefdf68-fe86-0106-213e-4d5fa24cbe2e\": {\n    \"name\": \"Excelsecu eSecu FIDO2 NFC Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3LWFiODctOTFjM2IzZjBiNzhlIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZWMxZTg3MjEtNzM3YS0wNTRlLWEzYTktNTFkMTMzNDZlZTI5IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MjE4NWYyYmYtODVmOS1jZjQ3LWFiODctOTFjM2IzZjBiNzhlIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyMTg1ZjJiZi04NWY5LWNmNDctYWI4Ny05MWMzYjNmMGI3OGUiIHN0RXZ0OndoZW49IjIwMTgtMDUtMjNUMTQ6NDA6NTUrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/0VxRQAAGfVJREFUaAXVwXfcn3V97/HX5/v9Xtdv3Ds7JJAIAULYBZmCimDVDlftw23HqYuqPV0WtdbWR63nVG2rnraOtshDrRUfPR3WWS3KVhAZYQoEQkLWndzzN67r+n7e504iKNWO858+n2nuisS/J3G8YZeZ2ZTEImD85+ROO0ZSUfiHJP6FHyIEWBjAwzNw6obI3CykCGaGJNyhLMWwgnropNJICBNUcooi0O8b+xfF6PLAqIMcGod2W+zYD9Fg49rAgb1i0TJTHWGCuo6UheEJdi9mVrSN8cKYq42d+8SKCSO2gAwdIBQQTPx7ZlDVdkkWbzTZcKTI3dhvvrGlueM9d8UTX0Rr+jmoyYCQOMSsBLpAAjLQRxpgxo+RAmlr4ocIZheGkF5lBpL4rwhICXLDfH+gDxeFkHgCCeSwf78hEz/KjMPED5IgRXuRuf20pYBZQ72f7StGH3YmTvxFMhcgAwliARLgGWwGNAfWQqwmhshBcn4sGOA+l8qCxxmQBU3DSZIj8V8TYFC0jYUFbe31dP2y5ZAzTxAS5MZAgPGjzQBB1YDxA9ZZ0KkmcEHImc93Lvi3HfHIkqZejTIgMEAO7l8nxk8h3YLn3YQ0jusM1LyOEM5E4seCgOz/lPYcEI9xQTtxxHg3nukYIL5rEdgOCCj4fgYSsR5qRaejq0Jiuqp4ghQNLw1V4seFAK9FMr5HQLTjQgybMciNg7Hn1pWXfOOh6sSL8PkjMQdLYGGawd7fJXYvR0WfEMAC1BWE4lZ6C/9Mmf6OcuTpSID4kWUG0m7Evem2bc5jho1YOxmPOnMTp2aJ7ICBiY8J/T7QAkYAcZAAQ8Eoc0O2yLbRUUMCM5CMdhv2zTlkI/JjRGARQhHIjXiMGcdKGneM0jKIOx6pV+/LZucj7xAMSPvo6xV49QXSOMzNw8gEdFowMwMjY5DSXprmrRT6B4xViB9dEktuJNqOtHc+8Jj+EDpd2xTajGgAGeMgd/9nYE8I4IIQQCwJgIMLXBANmgySkR2K4Nz9IDw6LzYfLQrjx4YZNDX0ek53LCBxSAp2jplhghY1szZx01XNBXMEthAqQBW95h006QvEEahJtMuXUMQX0FRX02p9hCLNowCersf8PrBV/KfEYcZ/nzjM+AHuEAL/ITlgYMZhBq6bEQvpSUdGHlPVxBVjdo6y4RIgENsEO6JBlpECVLUTghFLQTYcIyMKQZMhG1QNFKX45j1iYtJoJUOV+CEMGAECMA+I/w8CXGCAO1jkv81YIsgOEoeIwyxAXYm5/c6qlYZnaDJH5czJhIBMmOAh3/jlgXVWQz6RYDAYXstC/Rd0lkM5AvI3UHTfRwBqfx4jo1uBL2IR6gDZG0IABO4QI2DgDiYOsQRykIMZP0jgGULicRYAgQvMOEQCMyha4BnkPIEEFqBoQa7AHUIEBDnficjppElxiIDIms6YnZkbaDJYMDz73cgfmWkCRYLJCP0+WAAKHmeAZEgQAgTjkNE2pAgShwjIAozjgZ9BOk+wzsBc7AO+gvikxKP8JwS4GDG4KEXOEqzqtPAA3zHjC4Kt/BcEy4Jx8WibM2JkKooaeAD4CuLbGBQlxBEjZkGf9XVtm4hgCIzZv+XFDz0YNp6NLaxEDmXns0yZEyoo0xnI/oicoakhRMBeg3wTUkn21RgnE8QhrQ4og2cHbQf24qwi2HqSBRqBADMe5w6pgM4YDHqQGzCDkCAVMOyBHCwAAgGxADl4BoscZqAMCGILwjhUPaFswA6C7mFJmnlUHOQZWl1Wj4yyRUEgkBtlyT2tqAN754W5sWRCcKrgDLDjgOUGCoGdGLcC/yp4hB9GEOCYqXZ4bW7sRdF0FGaGIAMpQsCeZYFfM7N3CP7aQHwfATmrRPZLrcivYGyWWVeCtZMgl5rK3pSiPobzh8CA7yMgi1GZXepur4zGpg2rYlnXAjeUhDsPWeTPLfLH1UDafm+mLoyRtv3EZNcmqyxaNCBuvT6euwPxMtRv4+rRG9xIMug0MNQBLNxPa2QLuYFqAMTnA8/noCIAxiEhgucDLPY+TjP4EuNj9+DWJ4RANXM6dN/CyLKzWJwFbyBEQBBLUIDFmQdxXUcq7sTCgGH/KPpzz6AzehIGNA2kNnjewfbbPsrY6vtoTz4fa16IBcgZWiOQ60fYfv+HmFhxB93Rn8Pzy3DdjrGdJam7MXCQBEXkDDPGcgUWwXAGfV1fW0Buay3y87g9v922Ew1bITcwgSAFQ8Jj4H6ZXVFLHwBm+S4HArx49TJ7R9kKxw8WwQKPk6BsQQGWzdYXo/GjdZOjMh82DpMgJjtp9UT8391kF+eGokjCJbIMlxBYrnVku2tvMw9HmvJrBQOWOFAETlnVDh9sWbigccNM1BnEkiAkkLEhBHt3GWwVmd+8d5vzxe/E9Myz7cyLz4fqESiV2Vls+PyeYm2PPk/FMsgHDPozWICqgm7nATy/gNk9r6Eon0d79Ek0FYcICAHEEoEPv8qjD7yTVcddw8R4QzWALBBg+WFmFr/KbHMFU+XzCAmygwUo0x72PfSXPHDn37LlKQ9h1idEwGFm1yo6x7yVsvtG6hkwoDP6NhZmLmfZxhYpXYzXIAGCaCC9i179FzTXQTrhQspN4IvfAuZZkrpdcZCgE2VnezZcImK0Onx1dtb+Lje6eNUK+2DCjq9dhBC05ADSiAXKVjSaRjQixGDHgr3T4FnAr0p82wWdyFtbI+G3TTbeuBAQgBAN5PMjLT53x4O6etsC+84/wdZOYi9tiO8yy7ci3chB4txWyz4S4cQiQOg6vR57TFyVgjyYXSRY1QAOdGJ8qaRrJPtoU3PQuSnYFaPRNmWDjDDYWdV+vRnZ4Gwz22BANZSVnfiqo47ls5POVfPLbO2KUdtMX2AGBQw6E9c0d+1dxdrjNtFOoDhCZ/957HhgK0efC6EG5x4Gi79OSh8gpKcR/dcou6fQn4fskCJQ/z3Ub2BqzU6aPowsO5bh4AJcu/Dmq7QnBvSZZ/vWtzN27Gl0JzcyWATZ9VRzb6bdvobN54qiBWqgGoIitEf3sOfAmxi3SLd9KVV/F63uVzj6LIjFOlRdgAUQEAMMq3vJdhVr1kJuLcMmn4oqoL4ZPIORGHCIGVNEThJgBtn9y8MBrx8ds7cFhXd2ohg2fmPO+nSQ3Qy2D9NkU9kpi42/oGyFi8pIkAtvxMSYnR+K+AkLzYtG23ZBuwxvyz2160aYQZFAUPV7/qmisD9nVLf1+vSne44sQNYVjeztpfHURn4TsM4svM/EiSHBTF/9hUX707Ktj4602IXIN9zVbJ4ai+/fcnS4sBqIxlW0Y3zdvgU+um3ajzjtKP4MbFMtkGnOs783hPDJEOxRSRgciXgbxksFlqKtaKf4wv5QV516rJ60yjmh2m9YEJTsfo9e/8h9BzaewRHzU4QCFFqE8Aa8uomiuIWmD56hLMDig7RHHuSWa7/EsP9RTnn6s4gGi/W1yN5IHOykM7GMhYU3s7j4UsRqilAgPk6Ov0673stR628nhxvI2kh3/CbmF1+LuI3xNeDh6VT9VyGORPlmGv9TJlbtxID54V/Saj8XfCdzexexNtTVWUTfgBmYQTDoDXfQ0zYmWpA2noP7CfhgHyHfjomDkjjMxPpAOA4Dz9wg8X7V+r2RTnz5Yq0Hds/lPxwp7TPBmOO7gkHlXHv3w/6xiSn/+VM2pbdXs/Ykj2I4EKEKW556UvHlmJioemorc0grQQOPHhj6W2nsb8qCx8UIMRi49tdZf1AUXDBWpomFSr9lFs4JCAvM7Zr1S/vzfHzDesMMEDRut873mrcop/cEWB8DzXRP93/qOi/OPzn9amvUnrwwC5ge8tpfBXyNJ7ob9DuYnWjYaZ7FYrZNMcNK2JKCjVdmdBnAgBsf0hHb2LLudaQDI1QVyKCz6mSOmfok7n+M/Et4/QitUeiOgzcg7WDY+z1yPomiXE9jf4hpB6b1pHg54yufwXAAZhANXC+nam4l8B6649BKB8gLMNd7J5Vuo4qREbuMwcJvY2EMi1CMXoSqDthlxAAdzdI0eyk732I4nOOuu2H96tNZtTwxrCAYxAQL+2/CrM/oauhVT6ZVdJhurqetA3QiOKQUje86xYwpwU7Hr20ne0v2dG4/6+vu/ipgG99lgFhiHNI4vUa6HPdv7hvwibFOODUBuRHjIxyRHeoGgkEMsGtG387B31h27GoJEODQbUO3Mu7dnlnZEWXBVLsdO5Y5Xh5eoCiKCDNz+UPT+/zjrZSQwIA6w9pJZzD0awfz+eeSaSwmcpXZNTVqp69ZYb8iB8+OR96dUvxaMEYlGWBLWJKBA3J924zTWOKoXDSnK9uYJAQEgwPN6NW7e2ugzdmQQSwR4NDubMb9r8jFVqI+AfYZot+H+nD0aSz5Bsq30BvsgvANmj3gfhRh+TShuRJ5BYiGAhgh6B6KBAasWH46X7/yc1jrK+x7ADY+8+XE+AcIwwRiSYZ2+UtIZ1A3MxRhAmkzln6fbdsaRIeiOJWDDJBDw4D22LcY9mB2DkJ6MrRgqnMzTX2AbByUkFjSwux0CQyfjm7PDeNh06DUF1p9vZzGpuWAQAYZMMAM3CEA3TZQsHWu1s/UMf/VUd1wSb+GQQ0GmEGIQApff3R/fu3KFdzlAjNQgGYIJ22AZpv40OfhwjMDzz3dLt25x+Ro4+rltiwPIXS4p13yJ1PzRrsFqQV1AwZ0S2M4BEk7DJFlrBiNxYvP54VkVizOiZBsEemngLME44D4nhooDM7iIAODxWgU0ThJAtwgwZfjJXdsDSe2CPkIVAMBMBDQDDkkdU7Euu+iHrwaeAmTozfgwGIFqIf4BKVP0x9C5jq8uY5Q8D3GIcpQlNCdWMnevcv49rc+yrLOIivXrmCyuIzKDRNgPK7JXeBczMAdsPsxu42NR4H78ZThFOoKMEDg7GB0fCsR2Lv/BI5YtxkL8J0br6O3PxMLDkpkDpqk0OkgYrCjrWMj9+3RTdMLevU4TK8eg7IFbpANhAhBWANmcMRyY6SA/oLYvMy31zle2Wu4hCXGYWZQNf73/YpLy5Z2lQFKjNACBehV0CmEAAdiyXndbnrp1unmj8pRzl7fsnbdwM55v3rdlvDoyRsMGjHYATPT0EqwcsKwEFEw3CCHQITV0eyiWuAGEUbKEH7aAQnMDAQOGGAsCYYAA5R9ayfY6Ql7umSU7RrmeHB7/aTbB1Pd55B7G3DLYLs5rA02AUTUgAtSsZHsL2bPgRtoHCxvAFtDsK0YMHlcC08ryL2E6hqL4qAQurgmiUXBsP8wvdYrqPbMsn7l1Zz6HFi25kJy3shgHkLgCQwQICAVsDB7Lb3eblathRBPYXbfCg6yCFZA/5E7Ge6+ndFTYM2G0xlrH0Nv5gBX/eO9PHw3dEY5KClw0LGBcCoYoJFOS+zcmT+9Y5e2r15hdDvG2nFjUIEBBphgUIt2aRy5yrh9u5jtiRPW8Ryv7HfdjIB4TDDDG3v4zl3DfWunjNFWoh2MJkLtEIEA9IYwVjK+6aj4f+gqnLZJN2XF1wzmhRVUDNnaTAMm6gXRzBmt0pA7VQ2rlhc0bmQXMQnPrOkNOc6CiIYHWBCqBMkMY4mExYAlo19l9Tms7WbT9dA/VrTt9BitW1XQsQyJ665ZPHUHzs9igxLxBoyrgQI4HvQBzKZwQVmA5Dy86yYqwfIWdOIFMHICsd0DQTVYhzVXgE1BmAVzzEaAI4EaYz/YDKk6FzpXcMHPPkznKCCtp9ofeZyAwCFyiAkCmeyR1LqdXPWY2QNmJ5DKhDtYgPbYkMXZ/4tFiCuAAz9BM4R+/0Y2n7OLdcdBKjkoyQBjM9A1RBbUiyyun7C7jl4LT1pjzC7AYAhmPEEwkKBqIDsEC78I9qc1jEeE+B530WmFX142mu6qc/6wAxlwAQYIqgxjHVa88qJwxUmrwmmPPly/eqodDySz5XUjYm3FiraWz+4WQSKZEVqgisMETaOOjGyoaHfFcNFGlBkLLDELg+x/Hcw/UgQ7KrsiQg4qZHm20e6W2ZxxSLdpvJ2d+wrs9TlDLA0GkUU1dzQTu6DiGJLNY3wWtA0MpPuBS8HOBYEE84t/QtH6OKuXQf9R8PZTaY+sYvb+BYYzMPKkfRTlPmI8HxzMQAb14MsEu5JQ3IL7y4iD80hjs7hVTO8B91tot2pSTMhABjSQ/XMU5VfBd7M42EIIl7Fm5RyjJXziz6CutvPcN2R6/UTTh8X9H6fV+RuqGaA/Tq5+gl4FqfUNLvz5/aQCJA5KJloW7GQzQxImY+j61oYjuNbN2DcLGJiBeJwBJTB0QQrW3bDC/qAswpuGtSXMOcjEfhkdoCPAXWPHLEvvne9jcj5iAee7hKhqe8bxa8L7WuviKffdnR/+5j360nOeTphMigxAYJV4aoxWFoTKlUEGBnII0X7ZjJcHVAmb2D/jfzbRsu8oWd+zuskgi/Yg+52jId6JGWYQgeyBPZXO3dANFwfRdTEm+TtapR8RzJ6R3eh0wfY3fGbfebddc+zLVlFrI4OqDWqDwAKgA8Bbwf8nKQVC61NUM59h1SS0OtAfvZii9QJMsLhtGckgNnNQ/jLKd0A8h5AXqPt/D91PEFOmGXYJcRliiTajZgr3abJdh/ROxG+hPEWIcyi8H5p3I1+kbqA//B3WroU7bzjAo/fD1BGw7bZPM6yOpCjOoan+lf7sB2lPQQR6u09gZORkHDD7JtUQqiGPSRaYDGZPFocZwkyr+xW/GQwrjEI8rhWMZYKVwOddfMhd58TC3rlqMpxfu2gaUQSjct0WsFcX0iuaaJfKRRa0IqNlN35g6P6zLn0O7CGDo8GeEYM9nRDG6LnPzuc3bZzioeZAXqbxsK1VhOXDSpjZBaXCR8z0Boc5lrizPJq9vSzt0ioTOy1jUGn20Wm/u73Btrfa3D+YtZOzYDTZa3pVmBs29rutksrMkBhPQb+4vh1+TzBlBlm6y4y3J2OF0BaLRr2YSSV3PbjqKV+bmVv3U8TekZgD8dm4303OEAOY/RuR62m1CtA81X4IU9BUmylb78fKZeQ+LH/yZRTDW6mb/eDTiLeT2qMMFobM7x6y+hTIfjTW/zgxnYsDFi6iGZ6C6d9opYzxxzS6imZwBGOj91OH2/DgZIdW+fsU6e20OrDnoROpdSWnPg3WbNpHtrexsDBCqzXHyCQ0DiHB/PRGxiZXYPVecvMQMr5fGhnV+oV5Oy1EDnFA2HGlwluiAcZhxiEu7TXZfULHhEKXE3ha5ayihmhGA9RZ/+TGb7jn78j9ESxeHCwcD2KYRTArkoXnuPjJAH2DtoKlgiUyWPRLJzv6h1gEFqfZ/8h2/c0Jx3NqUZJyA2Z6hdAWI/yrRLdT8EzHNsug0zKiaWeKegnGLQMpDOa5ciTYybULi2bdMv5GnXWhYVeDumZ2tsxOG41K2aGW3SDpJRY0INh5YAgDBwL3rIr7Fqk4DUtgBjG+mex3In0RM8iCfjNgcGDA7COQa5C9iFi8D1tYj9cgQWfiEurp9+LVH5HCvZg5+Bz9Piz0l7GOX4D8FhpbjsQhRiIW76YZ/gIp3oXUYM31pBLm52FQQXtqPa3wv5C/FDOYmYbTnv3bxPYOegsfYd2xMKwyg2qelj2bOh+L6y9ot0RafRG5BuVv4HoYxPdLuw9w3nhbHXcwQIIiQpFgWAl3sMAQ8Yjg9ib7rkQYiYU9H7N1LhEEjXDQ9YtDf380PtNqBc9AI+0I2X8ppXC5sGMdIQlxSBSMGlCYMWg0bda8voU+7dnwDJ0Iew7oY2saf9rqkfhzvVknm8zgzGDhTAEREYNRZdEfautYl1enxHWGyAfcLdtfxzF7Vtm28/p9sSSmZOe4cw4YBzlGPwt3/5cQwpswtg1rJmIRnhmCgaATKmY0ddvn9TwoOQvmOURaTQyXI/8Y8FVcDzB0GM6vYzg4hbXHP5MmP5O8WBITh5hBNQ90foGyfSGevwi2C29Ed/xIyvYFDBePBkpCAnGYZ7B4FmX7M8DloOsw7Samkrn+MXj9FLrpeeDH0TiYgWdojXao6/cSeDbD3q1kb2iXx+P2XFKMiJ8m2DixPA014NxMtlmMJ0jb9tnZZxxnDOfkBBQCw2GjhcVK02WyngVlyeYxTHBcCuECC4zWWVni3mS6rwjcOZe5vsq6Osr2SeIxBpi4buD5xQG7LJm90MFSMCRwiSLSm6n1jwuV3ruyxc0skURrMtDpGidMsZCC/aqyzwq9MkUrzI1GAoxa0E7a45Wu7A/1J2PdcD8CBKpEu9SOnMPL983z5xNtPSsRGGYoAkjgEgm/Z99QHy4jl3eD7R9UjmACOBWJQ8TiPlv+2ft13BbE6YQaCDXuhtkaiuLNoNeQwn5GCqNYPsmyI8aIRaLuQ64bQiEQhxlgEexoTK/joJyh1YGRSRjMC1ETAk+kQExbUH4XhBkIs7hKppYvw2wEr1nimDWAESIMemA2SozPR/58YoQEuACDYJcgB3OWOHAdQfx7afPq8MFqUZ/EaEAKwRZ7feYXKy0eudKyGpsaVkzGSNtgBOTIpptGM2ALKXEAmHfRuKBgifFEBln6lsP/kOuKYPaUoeuoEGwYpHvqxr9eK9zkMDS+TzSsMDoJAuz2rDcOh/nvKsVnWNDxLQiYpt11izJfk7TVzDKPMSAABiHw4N45veThPf6TW9bylLJgw6DCzNiZTNeY+HqWHhLG9EJN3YiU7MBIaa8RgSAlEotfqJ91813941fQ7b+SQMZVAYZkmLWRuhhtygQh1BiLVIsDjExIgPNEDQgDEpAIBrluyE2DmTCWiB+gJgAdjBHMEpKIcQj0aOohZg4YjzGWyJAiUCAHUQMNB0kRcEQbbBa4iR/i/wH3D5PMpd2t5QAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"62e54e98-c209-4df3-b692-de71bb6a8528\": {\n    \"name\": \"YubiKey 5 FIPS Series with NFC Preview\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"ab32f0c6-2239-afbb-c470-d2ef4e254db7\": {\n    \"name\": \"TOKEN2 FIDO2 Security Key\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"973446ca-e21c-9a9b-99f5-9b985a67af0f\": {\n    \"name\": \"ACS FIDO Authenticator Card\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"74820b05-a6c9-40f9-8fb0-9f86aca93998\": {\n    \"name\": \"SafeNet eToken Fusion\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"1105e4ed-af1d-02ff-ffff-ffffffffffff\": {\n    \"name\": \"Egomet FIDO2 Authenticator for Android\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"08987058-cadc-4b81-b6e1-30de50dcbe96\": {\n    \"name\": \"Windows Hello\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMDc4ZDQ7c3Ryb2tlLXdpZHRoOjBweDt9PC9zdHlsZT48L2RlZnM+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIyNC4yNSIgeT0iMjQuMjUiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTMzLjQiIHk9IjI0LjI1IiB3aWR0aD0iOTguMzUiIGhlaWdodD0iOTguMzUiLz48cmVjdCBjbGFzcz0iY2xzLTEiIHg9IjI0LjI1IiB5PSIxMzMuNCIgd2lkdGg9Ijk4LjM1IiBoZWlnaHQ9Ijk4LjM1Ii8+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxMzMuNCIgeT0iMTMzLjQiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjwvc3ZnPg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMDc4ZDQ7c3Ryb2tlLXdpZHRoOjBweDt9PC9zdHlsZT48L2RlZnM+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIyNC4yNSIgeT0iMjQuMjUiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTMzLjQiIHk9IjI0LjI1IiB3aWR0aD0iOTguMzUiIGhlaWdodD0iOTguMzUiLz48cmVjdCBjbGFzcz0iY2xzLTEiIHg9IjI0LjI1IiB5PSIxMzMuNCIgd2lkdGg9Ijk4LjM1IiBoZWlnaHQ9Ijk4LjM1Ii8+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxMzMuNCIgeT0iMTMzLjQiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjwvc3ZnPg==\"\n  },\n  \"a4e9fc6d-4cbe-4758-b8ba-37598bb5bbaa\": {\n    \"name\": \"Security Key NFC by Yubico\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"0acf3011-bc60-f375-fb53-6f05f43154e0\": {\n    \"name\": \"Nymi FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAALFAAACxQGJ1n/vAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAjRQTFRFKb7GKr7GK7/GLL/HLb/HLsDHL8DIMMDIMcDIMcHIMsHINMHJNcLJNsLJNsLKN8LKOMPKOcPKOsPKO8PLO8TLPMTLPsTMP8XMQMXMQcXMQsbNQ8bNRMbNRcbNRsfOR8fOSMfOScjOS8jPTMnPTcnQT8nQUMrQUMrRUcrRUsrRU8vRVMvRVcvSVczSWc3TWs3TW83TXM7UXc7UXs7UX87UYM/VYc/VYs/VZNDWZdDWZtHWZ9HXaNHXadHXatLXa9LYbNLYbdPYcNTZcdTZctTZddXad9bbetbbe9fcfNfcfdfcf9jdgNndgdndgtnehdrfhtrfh9vfiNvfitzgi9zgjNzgjdzhjt3hj93hkd7ikt7ik97ilN7ilN/jld/jl9/jmODkmeDkmuDkm+HknOHlneHloOLmoeLmouPmo+PmpeTnpuTnqeXoq+bprObprebprubpr+fqsOfqsefqsujqs+jrtenrtunst+nsuOnsuersuurtvOvtvevtwOzuwezuxe3wxu7wyO7wye/xyu/xy+/xzO/xzfDyz/Dy0PHy0fHz0vHz0/Hz0/Lz1PL01fL01vP01/P02PP02PP12fT12vT12/T13PT23fX23/X34Pb34fb34vb34/f45Pf45vf45/j56Pj56fj56vn56/n67Pn67fn67fr67vr77/r78Pr78fv78vv78vv88/v89Pz89fz89vz99/z99/39+P39+f39+v3++/7+/P7+/f7//v//////Wpo4rAAABClJREFUGBmlwY1/lAMAwPHfdlua2mWkFnVHShEqxIhiUipvkTo0RGJUWF4yUd6Z92rztqJSmBq2pmf3++c8z+1Wd8/urtun7xfPE1Zw6mB3V1f3wVNWgKUN7M20zKwlp3ZmS2bvgKVhCUOdy+qJmbCsc8gScIy+tiZG1ExNXbsgNbWGEU1tfzkGxgw+MYlIas3r3w6YM/Dt62tSRCZtGjQGi703i9C0R7uNOfDoNEKpPRbDQkMPEZr14ilLON1xJaGVAxbCAgfnA5NfDCwj2DoJuOaQBfCsA9OApUes4PBtwPQDnoVndCUhsSVrRdlnE5D83DNw1PcXQcMez+n9SdC431GYd7gZkp9Zhc+SMOOIeTgiWAQTP7Eqn18IiwNH4IiNUPuuVdpdCxlHYM5XCchYtQ1Q22UORoIFsCiwasFCuG7YCEa2Qd33jkNPHWw3gqHTM2GD47IeZgWGMPQaTD7huJxMQochDF0LGYsdvXX2q1aSgQWGUHug7pjF7gM6rOBYHfSoqI/BncbMBRqPWsGdsFFFnQO7jEkTWmEFb8FcFT1eQ+KEMWki71neiQQ1xxTdBdcbl4a5kBq0vOvhbUUfh3XGpWFvI2Qsbx08rmgrbDMuDd3tUN/jqKGjvXknzdkG9yg6Hz4yLg3dwXWwKGtO7/J6RtW/a+RDmK/oDPjJuDR0+3UCthv5YQoF1hj5EWYomoTfjEtBjz4EFx03dDvQNCXv6n1GjkJS0Tr425jBBjii/c2wUv0nQc1eY/6BhKIN0Gdk+J1teS/dCs1ZtRNqPtCfYZpxfTBR0anwi5HNFHrByB1w5ZA9kDLuEFyqaBr2GXmEs2oezho51ACb7IGUcd9BWtEl0GnkxMa1efc/td+852DCjz2QMq4TblH0AdhsWcE8uKkbUsY9Aw8q2g6tltdVCxsgZVwrtCv6BTQNW94aqIOUMdlL4EtFg0bYZ3l9UwmljPkOkoGiLoeMFewklDYmA3epqG/AZcOWl10K3GSx7Ex4S0UdmAx7rKBvNrxhsT0weVDF0FpYZCX/vvmpMQthrSEM9SbgA8flfUj0GsLIvTDntOMQXA0rjWCk9wJ43nHYAhMPGsGcNpjwjVXbPxGeNgdzTs2GK/qt0sk0XDVkDo7oboAlQ1blvxa4YJ8jMG8HsCKwCsEK4FXzcNQGYPmg5zR0D5BxFI7KrgJu/sNz+P1GYFXWUXhGcD/Q/IkVfdwMrAo8Aws8ASQe+duy+tclgCctgIU6G4HmV05b0n87pgPJdyyERXpvIHR5e59j/Nl+GaGFvRbBYsPbmwjV393xqwV+fe2uekIXv5K1GMb1PTmFnNSy9S/v3L1758vrl6XImbLpL+NwrP6t8yhh3tZ+x8KS9rctrqdA/Y1tBywJyxno6sisbm1paV2d6egasBw8T3ie/gevj4H2FDP02AAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3\": {\n    \"name\": \"KEY-ID FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAASCAYAAAAKRM1zAAAEGWlDQ1BrQ0dDb2xvclNwYWNlR2VuZXJpY1JHQgAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VQNcC+8AAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAADqgAwAEAAAAAQAAABIAAAAAcdLtCwAAAzhJREFUWAntV2lIVFEUPm/GcRobR8n60Y8UlSDbSMkWWsSSIAzMMSlJEA2LbDE3bBEKasiSjPmjiLRQZhiN5o9MIavJIKHBCUuZjBSpftgkhZPkMjP2Fu9579q8yUQhsQuP+33nfPecd+659zHDjLED5sBQzIEa+RLnTKE+0o5WtD+A4ocG3qTVLYI3RxrQHXoxGnHUsq1gSrwCUhs6x4E/u94xYBcYMwY9Jy0oCSuOBnJhtLogNk8j+qRAGr/n1CvelVyXDxabGWWMQgkMI9D3BS9BQQgqBEB11A0ucLuc488oSpNqc9EO4OaL5JyilqwR53Z2k9DvdEGbvYuPV9/9HFxOUSdX5MT4/GIu55hbjMu+q2t0GJwjwhNqiILY2+lESs1UoZRnnFj6bGDpfIoua664m2iUARnbD6Mn/X4ej49XZ6Mta8cJxNMFuntfQ1KdkEsakzq6UgfBSZUpBIJ+UyoEqrXIpaC3yCqlPD678SBcby7n8ff+T2C1v0ON2i8ACtelIZ8KkOYsMBvhXstNPoyl4wlAIh3Ra0e5I0uGWqOFq7G/7xTxy81FCefQtbtiH+K2Y48QTwcoickGhVLsW6+jjworeigzwNCQgzqyb3PYXfIyQi5EolepUkN3YSvPM1clwKXGEhgdHkR/WMga0Ko0UG1rgg77B7RzIDhgMRxaPaEdlEKe+M0PhB8DX3lBRv1paE69hmLZQrkLToaPrwZ8FSpC/3q25Zsh3LAW15mSjTw2dTZRm8kZQ5asmHKhmMADkD26Wt1CUKqE4pwjP0HaMQ9xgLtz5NFo/CmJD6Ok+IJ5OopPF5H+yFOzp0o6ZDvKiS7riyGvRryXZ1rKwLAlS7oecVfuM8STBSZ9KYB+smrvOqO1BgYd/Shq2FuGmANeC92zdBsoUkoh567wUaoyV0LK8p2wMiiUCsKRzTf2U7YX6XcoPhOEy/nN8QXvJcmRGXeUQJxljy5R6MNjISZyF6EQX+65BR8/d4L0wQUzCLh85OND0gSzd7xowwFCcf5joZzyVvx59meWKI0wxmGAfwGo1BqICF8PrTmPoSWtyuMrMf//pnncl9lrFM/j7K1hUm/+C10yKn106Y1DAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"4c50ff10-1057-4fc6-b8ed-43a529530c3c\": {\n    \"name\": \"ImproveID Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"ee041bce-25e5-4cdb-8f86-897fd6418464\": {\n    \"name\": \"Feitian ePass FIDO2-NFC Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"efb96b10-a9ee-4b6c-a4a9-d32125ccd4a4\": {\n    \"name\": \"Safenet eToken FIDO\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"4b3f8944-d4f2-4d21-bb19-764a986ec160\": {\n    \"name\": \"KeyXentic FIDO2 Secp256R1 FIDO2 CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"4c0cf95d-2f40-43b5-ba42-4c83a11c04ba\": {\n    \"name\": \"Feitian BioPass FIDO2 Pro Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"5343502d-5343-5343-6172-644649444f32\": {\n    \"name\": \"ESS Smart Card Inc. Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"09591fc6-9811-48f7-8f57-b9f23df6413f\": {\n    \"name\": \"Pone Biometrics OFFPAD Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaMAAAGjCAYAAACBlXr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAHTmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4wLWMwMDAgNzkuMTcxYzI3ZmFiLCAyMDIyLzA4LzE2LTIyOjM1OjQxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3YWY3MjAyNS0yZDJhLTZjNGEtOWYyZC0xMjFiMjFjODUwODciIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2MjZhNDA1ZS1iYTlkLTg1NDAtYTcxYi1kNGVjOWM3MTUxNDIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZjI0NDI5MDctZDViZS00MWVkLWI1YmEtZjllOWM3YzkyYjUzIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChXaW5kb3dzKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjItMTAtMDZUMTM6MTg6NTgrMDI6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2ZDhlZmNhLTMzNzItNjY0My1iMjhhLTU3Y2QzOGJkNzBhMiIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjkzMmZjNmE4LWYwMjctMTFlNC1iOTc0LWQ5MmNiZGU5ZmNlNiIvPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyYmYwNzYzNC01MTk3LTRlYjYtYmY3Yy1mOGZmOTZkYWJkMmQiIHN0RXZ0OndoZW49IjIwMjItMTEtMDNUMTE6NTc6MzMrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpmMjQ0MjkwNy1kNWJlLTQxZWQtYjViYS1mOWU5YzdjOTJiNTMiIHN0RXZ0OndoZW49IjIwMjItMTItMTRUMTE6MzE6MjErMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT54bXAuZGlkOjc5MDY4MzA0NzNCODEx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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaMAAAGjCAYAAACBlXr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAHTmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4wLWMwMDAgNzkuMTcxYzI3ZmFiLCAyMDIyLzA4LzE2LTIyOjM1OjQxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3YWY3MjAyNS0yZDJhLTZjNGEtOWYyZC0xMjFiMjFjODUwODciIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2MjZhNDA1ZS1iYTlkLTg1NDAtYTcxYi1kNGVjOWM3MTUxNDIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZjI0NDI5MDctZDViZS00MWVkLWI1YmEtZjllOWM3YzkyYjUzIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChXaW5kb3dzKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjItMTAtMDZUMTM6MTg6NTgrMDI6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIyLTEyLTE0VDExOjMxOjIxKzAxOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2ZDhlZmNhLTMzNzItNjY0My1iMjhhLTU3Y2QzOGJkNzBhMiIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjkzMmZjNmE4LWYwMjctMTFlNC1iOTc0LWQ5MmNiZGU5ZmNlNiIvPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyYmYwNzYzNC01MTk3LTRlYjYtYmY3Yy1mOGZmOTZkYWJkMmQiIHN0RXZ0OndoZW49IjIwMjItMTEtMDNUMTE6NTc6MzMrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpmMjQ0MjkwNy1kNWJlLTQxZWQtYjViYS1mOWU5YzdjOTJiNTMiIHN0RXZ0OndoZW49IjIwMjItMTItMTRUMTE6MzE6MjErMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNC4wIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT54bXAuZGlkOjc5MDY4MzA0NzNCODEx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\"\n  },\n  \"7e3f3d30-3557-4442-bdae-139312178b39\": {\n    \"name\": \"RSA DS100\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"73bb0cd4-e502-49b8-9c6f-b59445bf720b\": {\n    \"name\": \"YubiKey 5 FIPS Series\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"149a2021-8ef6-4133-96b8-81f8d5b7f1f5\": {\n    \"name\": \"Security Key by Yubico with NFC\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\"\n  },\n  \"b90e7dc1-316e-4fee-a25a-56a666a670fe\": {\n    \"name\": \"YubiKey 5 Series with Lightning (Enterprise Profile)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"175cd298-83d2-4a26-b637-313c07a6434e\": {\n    \"name\": \"Chunghwa Telecom FIDO2 Smart Card Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"3b1adb99-0dfe-46fd-90b8-7f7614a4de2a\": {\n    \"name\": \"GoTrust Idem Key FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAjCAYAAAD17ghaAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACHDwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKL2lDQ1BJQ0MgUHJvZmlsZQAASMedlndUVNcWh8+9d3qhzTDSGXqTLjCA9C4gHQRRGGYGGMoAwwxNbIioQEQREQFFkKCAAaOhSKyIYiEoqGAPSBBQYjCKqKhkRtZKfHl57+Xl98e939pn73P32XuftS4AJE8fLi8FlgIgmSfgB3o401eFR9Cx/QAGeIABpgAwWempvkHuwUAkLzcXerrICfyL3gwBSPy+ZejpT6eD/0/SrFS+AADIX8TmbE46S8T5Ik7KFKSK7TMipsYkihlGiZkvSlDEcmKOW+Sln30W2VHM7GQeW8TinFPZyWwx94h4e4aQI2LER8QFGVxOpohvi1gzSZjMFfFbcWwyh5kOAIoktgs4rHgRm4iYxA8OdBHxcgBwpLgvOOYLFnCyBOJDuaSkZvO5cfECui5Lj25qbc2ge3IykzgCgaE/k5XI5LPpLinJqUxeNgCLZ/4sGXFt6aIiW5paW1oamhmZflGo/7r4NyXu7SK9CvjcM4jW94ftr/xS6gBgzIpqs+sPW8x+ADq2AiB3/w+b5iEAJEV9a7/xxXlo4nmJFwhSbYyNMzMzjbgclpG4oL/rfzr8DX3xPSPxdr+Xh+7KiWUKkwR0cd1YKUkpQj49PZXJ4tAN/zzE/zjwr/NYGsiJ5fA5PFFEqGjKuLw4Ubt5bK6Am8Kjc3n/qYn/MOxPWpxrkSj1nwA1yghI3aAC5Oc+gKIQARJ5UNz13/vmgw8F4psXpjqxOPefBf37rnCJ+JHOjfsc5xIYTGcJ+RmLa+JrCdCAACQBFcgDFaABdIEhMANWwBY4AjewAviBYBAO1gIWiAfJgA8yQS7YDApAEdgF9oJKUAPqQSNoASdABzgNLoDL4Dq4Ce6AB2AEjIPnYAa8AfMQBGEhMkSB5CFVSAsygMwgBmQPuUE+UCAUDkVDcRAPEkK50BaoCCqFKqFaqBH6FjoFXYCuQgPQPWgUmoJ+hd7DCEyCqbAyrA0bwwzYCfaGg+E1cBycBufA+fBOuAKug4/B7fAF+Dp8Bx6Bn8OzCECICA1RQwwRBuKC+CERSCzCRzYghUg5Uoe0IF1IL3ILGUGmkXcoDIqCoqMMUbYoT1QIioVKQ21AFaMqUUdR7age1C3UKGoG9QlNRiuhDdA2aC/0KnQcOhNdgC5HN6Db0JfQd9Dj6DcYDIaG0cFYYTwx4ZgEzDpMMeYAphVzHjOAGcPMYrFYeawB1g7rh2ViBdgC7H7sMew57CB2HPsWR8Sp4sxw7rgIHA+XhyvHNeHO4gZxE7h5vBReC2+D98Oz8dn4Enw9vgt/Az+OnydIE3QIdoRgQgJhM6GC0EK4RHhIeEUkEtWJ1sQAIpe4iVhBPE68QhwlviPJkPRJLqRIkpC0k3SEdJ50j/SKTCZrkx3JEWQBeSe5kXyR/Jj8VoIiYSThJcGW2ChRJdEuMSjxQhIvqSXpJLlWMkeyXPKk5A3JaSm8lLaUixRTaoNUldQpqWGpWWmKtKm0n3SydLF0k/RV6UkZrIy2jJsMWyZf5rDMRZkxCkLRoLhQWJQtlHrKJco4FUPVoXpRE6hF1G+o/dQZWRnZZbKhslmyVbJnZEdoCE2b5kVLopXQTtCGaO+XKC9xWsJZsmNJy5LBJXNyinKOchy5QrlWuTty7+Xp8m7yifK75TvkHymgFPQVAhQyFQ4qXFKYVqQq2iqyFAsVTyjeV4KV9JUCldYpHVbqU5pVVlH2UE5V3q98UXlahabiqJKgUqZyVmVKlaJqr8pVLVM9p/qMLkt3oifRK+g99Bk1JTVPNaFarVq/2ry6jnqIep56q/ojDYIGQyNWo0yjW2NGU1XTVzNXs1nzvhZei6EVr7VPq1drTltHO0x7m3aH9qSOnI6XTo5Os85DXbKug26abp3ubT2MHkMvUe+A3k19WN9CP16/Sv+GAWxgacA1OGAwsBS91Hopb2nd0mFDkqGTYYZhs+GoEc3IxyjPqMPohbGmcYTxbuNe408mFiZJJvUmD0xlTFeY5pl2mf5qpm/GMqsyu21ONnc332jeaf5ymcEyzrKDy+5aUCx8LbZZdFt8tLSy5Fu2WE5ZaVpFW1VbDTOoDH9GMeOKNdra2Xqj9WnrdzaWNgKbEza/2BraJto22U4u11nOWV6/fMxO3Y5pV2s3Yk+3j7Y/ZD/ioObAdKhzeOKo4ch2bHCccNJzSnA65vTC2cSZ79zmPOdi47Le5bwr4urhWuja7ybjFuJW6fbYXd09zr3ZfcbDwmOdx3lPtKe3527PYS9lL5ZXo9fMCqsV61f0eJO8g7wrvZ/46Pvwfbp8Yd8Vvnt8H67UWslb2eEH/Lz89vg98tfxT/P/PgAT4B9QFfA00DQwN7A3iBIUFdQU9CbYObgk+EGIbogwpDtUMjQytDF0Lsw1rDRsZJXxqvWrrocrhHPDOyOwEaERDRGzq91W7109HmkRWRA5tEZnTdaaq2sV1iatPRMlGcWMOhmNjg6Lbor+wPRj1jFnY7xiqmNmWC6sfaznbEd2GXuKY8cp5UzE2sWWxk7G2cXtiZuKd4gvj5/munAruS8TPBNqEuYS/RKPJC4khSW1JuOSo5NP8WR4ibyeFJWUrJSBVIPUgtSRNJu0vWkzfG9+QzqUvia9U0AV/Uz1CXWFW4WjGfYZVRlvM0MzT2ZJZ/Gy+rL1s3dkT+S453y9DrWOta47Vy13c+7oeqf1tRugDTEbujdqbMzfOL7JY9PRzYTNiZt/yDPJK817vSVsS1e+cv6m/LGtHlubCyQK+AXD22y31WxHbedu799hvmP/jk+F7MJrRSZF5UUfilnF174y/ariq4WdsTv7SyxLDu7C7OLtGtrtsPtoqXRpTunYHt897WX0ssKy13uj9l4tX1Zes4+wT7hvpMKnonO/5v5d+z9UxlfeqXKuaq1Wqt5RPXeAfWDwoOPBlhrlmqKa94e4h+7WetS212nXlR/GHM44/LQ+tL73a8bXjQ0KDUUNH4/wjowcDTza02jV2Nik1FTSDDcLm6eORR67+Y3rN50thi21rbTWouPguPD4s2+jvx064X2i+yTjZMt3Wt9Vt1HaCtuh9uz2mY74jpHO8M6BUytOdXfZdrV9b/T9kdNqp6vOyJ4pOUs4m3924VzOudnzqeenL8RdGOuO6n5wcdXF2z0BPf2XvC9duex++WKvU++5K3ZXTl+1uXrqGuNax3XL6+19Fn1tP1j80NZv2d9+w+pG503rm10DywfODjoMXrjleuvyba/b1++svDMwFDJ0dzhyeOQu++7kvaR7L+9n3J9/sOkh+mHhI6lH5Y+VHtf9qPdj64jlyJlR19G+J0FPHoyxxp7/lP7Th/H8p+Sn5ROqE42TZpOnp9ynbj5b/Wz8eerz+emCn6V/rn6h++K7Xxx/6ZtZNTP+kv9y4dfiV/Kvjrxe9rp71n/28ZvkN/NzhW/l3x59x3jX+z7s/cR85gfsh4qPeh+7Pnl/eriQvLDwG/eE8/s3BCkeAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIXRFWHRDcmVhdGlvbiBUaW1lADIwMTg6MDU6MjggMTY6NDI6MTT9hwrfAAAIHUlEQVRYR51XC1BU5xX+dllgQd4PURAfiShaNG1i7Bhtm05KUknTWB+NQa0YG2ODljoOGk1iO51qNGQck9okRJs04Iw6puN0TExTaOsYS7SSphpf1KAVBRZhWR4rILt7b7/z37vsQhaC/S7/svz3vM/5z/mx6ASGCZ2P/Fgs8pf66INfjMV4OWxYzd/Dg+ZXYEHlJ5/jvgWb8OjqHWhscan9O1UuGF4EhMQU3trhRt7ql3GqshpIiAF8PqDrNpYV5OH1F1cgJjoqKFLCI+IHN2x4ETCV/3zbH5A8cRFOVV8CRicDUZFANJfVivIDFaj69xeKTikkj6bRFH1w5YJBItDf6j9Vnsa8Z3bQWy8QS6+t5jt3t4rA1s0F2LzqcWOP6L1ap4yKGDfG3CEGC4QYEAyNjx+115v0KY+u15GWpyMnX8c0WUt1ZD+hI+lhfWHRTt3r9ZnUBhpXbdTPIVw/jxG6Y80Wc5dyfQG5wRi0BvKLd2N/2QfMcyxgZ5gFku+WdoycOAZV+3+NuzPTjH3CtfsdONYW01EfwpDAHY1PB/+2IWNfKeKXzDcIB8CiMVHB1fv2H49hZWEJMMIOxIzgDu3TWP4dXTTEhvJXirD0sTkGMdFTfQZ1314AX3cjFbMu+ClQhahi7uXTgsjkiRhz7BDsOdnqDVgfFqayLwJfXG/C7CW/ws3LzF9KolGe8qanVylfu3YhXnu+QEgVvM2taJj3FDqrjtLHVO7Y1L5EwId2qrZQRLz6NPY93G9GbO4iZB4tJ3mYMq/PAMu4H9HDCK5wQ7GPXje1YsaD96LinReYiWghU3Csfg7O0tfoawyFRCtBugq5C2HWRGRWHYbu9TEy86Fr7aRL4nsxiWJpnC0pA1nOc0qWMq++ycWz3ANEmsp7bsMWbsXHH+3C6fe29Slve/cQLlji4Cp9i/6mkFmUi89urjaM3Lodk3x1iPrmfYiePRPZvhsYub2EKWgmt4eUOnli4Wmtg+ZmSgkVAYezDaNzlgJpSTxDXqSPTkL9X3crAkH3yc9w44cr4GmuUeEWMYY33arQEn9cgPSDbxjERAeFh9msLCPWkYnajBnwNTSRL4wGtWNyVyOsUXYzQSJOMqGWxv7CVJi4NmsersyaBa35JpVL1QuLF71ogH3a1zCprraf8pK3jyB+aj5i6NDrbE5+2Mam01ivioJRnLLMFCioPWPTLAsF90kpslH8JkdRwu1UQib8pQITzv4N4Znpiu5E9UVE5ORjw5a9QBxTFhGOwk0Bw+QIG9L7I2CA6AxS7EcY7GSUEpIi60bq9h3I1usxIvc76v31my5Mm7cB33qkCB5hT44jE48ij5hNDPkKBAwYBMoutXgq6FXKxmfVvqB9cSHG3rMM5y5eAzKYnrBQPgbwZfcGScFAyAFSj8Ugb311Dy5aYuA+eAjW9BTj9IiBbp6kLs4HvyZpYEEYOgXsTAMZBMIk3iuZ1khcuesBNP5iHVOTyHnDwSRGd7NZOVwoLlyAjT9bQCN4xCgqMtxoTn5I7RhFGEDAAE4vtQZATLLKY2Hn6vbAw0knPUB2da0XWkML7v16Ftpq38PL6/PZiGiQMPGXPVwiE4CSwycYQREgV4giNDocP3k8jW4mvV5Tp8Edl4DKD3bi00NbEW82K1cnvTfHdbA0+S6S5AlG/wiEqAGbmmyGajkNGjpV10v77W5Maj+Hh76RpejaeTeYtfgFvPH7I7ykRCmeYIjkr45AiBqQrqWhh+J62EwbkLByJabqHUhaExhMT/9yDxLGPY6T/6phD+AEFW2sqc5bRrsVDB0BCX1QDdg4qfzIdrG3T78HEVOmYHJzE0bt5ag28dbBSlgmzMfesg+BdE5EuTdIFCUNnCclxctMSm5TthHF/lFWGlXqmWP1hU3k8jUH/nzijLxCWEIixp9h17vwd9hSOCuI059fQcoDq/DMul28MzDcfq9v8zTcaMaSRd+FfvUwipbnKXqBt1EGEgt3QGqUAZGR9FjGr4AFpDMVcxc+hyk/KEadw2nsE228F8xc/CJmPlQIZ1uHeW+gCC95G1uRM3k86i/tx74da0wO8rxZzgkaD2/dNdoYriKgM7HQeLsi+m5EuSt+w4r+B5BqCpVKFo+a2/DTZ+cjlS32pa3vAolBVzSpmXY353scjv5uA3LnTDf2ia4Tp1D/yFJ4uhpYyMlUakxQL0e3LT4Fk9p4syZMA9RXlB05geUbOIaloyWaTUZwi91NGlWMjFdzT/JMbNu8HJueDtyIvc1O3Ji7DLc+reCBTSO1TXGI1x7cROyM7yHz48Ow0AnZVwYIY/C9sLhkH155qYyDhUcwiqNZveOSOun1sOs58cRTj+HAziKDwUTjT9bBVV5KxXGktlOp8PmouhUR9jRkVB7gReV+g1jqTeTKhSQUvJpPn/3kFl7J5xrX8KlPqu9Z31+nO1raTCoDzlf38Cpu51U8Ua9BJtdY/RLXBf59HrG6s7TMpJRrf/9r/JcMkIjwpw/V52v11DmrdQv/L3j/+GfmroHOiuP6f2KzqCRaKazBeK5x+kWkcS9KbyhYb1IKRK6xgjHo/wVDwcOrVb3k+exxhjuFgZahI2Ikz02IuT8XY97fB9tIKT6VvEFhdJ4hISICNjatfR41GaPQffYs1Y7uU64xz9YIO+6q+gTj//mhoVx8C7CGhkTgTnD78n/1q9MfZs4jGepUhjqeuU7Snbv2mhR3hjsyQGNh+jPo/uiYXpeXrzuKtgT9Nxn6/7+h8H/VQCiIkKFyHRrA/wC4e+O+Z1cn4QAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"998f358b-2dd2-4cbe-a43a-e8107438dfb3\": {\n    \"name\": \"OnlyKey Secp256R1 FIDO2 CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"61250591-b2bc-4456-b719-0b17be90bb30\": {\n    \"name\": \"eWBM eFPA FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"8c39ee86-7f9a-4a95-9ba3-f6b097e5c2ee\": {\n    \"name\": \"YubiKey Bio Series (Enterprise Profile)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"f8a011f3-8c0a-4d15-8006-17111f9edc7d\": {\n    \"name\": \"Security Key by Yubico\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"8976631b-d4a0-427f-5773-0ec71c9e0279\": {\n    \"name\": \"Solo Tap Secp256R1 FIDO2 CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"516d3969-5a57-5651-5958-4e7a49434167\": {\n    \"name\": \"SmartDisplayer BobeePass FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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MIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhOEV86zZ/8PMp0hD/Ud//AAAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,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MIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhMEA4jwBMEAIjxBMIAITxAMIMITBAOI8ATBACI8QTCACE8QDCDCEwQDiPAEwQAiPEEwgAhPEAwgwhOEV86zZ/8PMp0hD/Ud//AAAAAASUVORK5CYII=\"\n  },\n  \"8681a073-5f50-4d52-bce4-e21658d207b3\": {\n    \"name\": \"RSA Authenticator 4 for iOS\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHsAAAAvCAYAAADD2LWeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxMAAAsTAQCanBgAAAATdEVYdFNvZnR3YXJlAEdJTVAgMi44LjgxgctiAAAcH0lEQVR4XsVciXtWxbn3j+mtypoQErIZZRFBFKu4VG2tVWn16lOfeitYbdVbbatdrLbuyuJGrUordaGV7BuBEPYICGQhgZCwE7J/yXvf3++dmXO+sOSLib3vk/lmzpyZd959lnNOLpIhESRkAZIuvh4MDSUj4ZX+DLrB/PWoIN7PlxWI05WSAG1Ah7+tEK4HY5UAdxlqh93+2qB4gMrQOUo9bp+TloHAh/2CF0288LmDYZepwkXDOw25IYcr62uDoiEmZYh5EtoBl18IBrSLKTHeP5RdGpREVHceaXiWglBdRcSr4x3JG8UYweMjuILx45MC6x3NULyXlYLP4/wZubH+KUKSsq08CgQcOI7BwFd5fF643puiHimMhcaume/HXH+i6xgeVmJESwTXEBmL+HECBf2W44a2Z66gOcxs7BDDqUCa3FgJ1sRoDxCrSyLBS9IoO5fsLwQWxh1y9tXkkaSKLLRD5hRq4InTfMhYI36Mh3bxpucB14vA5uim40VdTWi4Njq8MBWYx5ROQNnThSYxWhyNgf+UIs+FAfjjCg7gCGAWL3NIOAhoiegMoBXEmaJu4uA82xMDsIH6ThyXrv17pbth34ipp3E/c7TvamqU0w37NTVKoqNDBoHaKyEAyudg5Lyg7WNGlMC1KiYw7LKB9nZp++xz2fvC87Jzyc9ky333SO2iG6Tmphtk4x23y/af3i/1T/5Smlcul8PlZdJ9+CD7eoMyxWA0XxOn+WuCIjQ6bU52pBrNPkHDmtuoJpvoHnpFdLCKFjF62i5CH29FSJ7dpneWS1HaxVKSm3nBVMw8I0rZ06Q0J0PKcqczL8/OlMr5s2T3kofl4KdrZWCwH+M6okcG3y5SiAGvcdHdIy0fr6ZCS2ekk4bSvOlSnKN0aA4ajLZMLWdKWb7SjHugUWmtXjBX9vzmV3J800ZFFvGfMoEjgilyYGBAZbqS41XfvEDT1bJh0XWycdG1Un3NfGn+aJUOa2P7oWHUKB8p/UL2vfiSNC5/Uw1HOXeGP9rIE+ZsGF/csppWrpSytMkUyoVSuUt2HTOAvCwpypnuhJ8lJdOnSsm0S6W8IF+aXn5J+jraONrIYBYcwhoI1b+B/i7p+OILqVpwhRRNm0zFleVmBRo4JlJuOukrmwFajDYqXa8rcrKo+OKMKVI47RKpmDtHmla8JgOnTlLIXjZjBlVQz6Gjsn7hlVKamSZlWWqUkIfSXJKTJsWZU2XDzYuk9/BhbWycWtAy3ltWvCn1v1gie5//nRyrXs863h4lgRcZQkMaIVBlr3pbiqdeTA/1iZ4yLEFYwavPkSDQolxlkJ6uws5WJtMmyMYbFkl78b8w2AUB5EQR3Ojs7Twpe559RkozJqg3K74wXjo9F4YX6rJBO5Se3AaKL8mbIaV6XZKTbbkKv+rKOXJi62aOFfnY2KH1g/elZOLFUpynNKiCIY8SlQ2NU2ksnPptOfz5v62xG9ZPK4c+el9a3nmHSu9pP8T7/t5oQJV9bjjw9kopTp9A4UBICMcQEMNgXnoQKBQeCTKq8/XwKuRkToVZnqN9gTMzQ6pm5UrLX1eR8IjyyPgIrAdj/baOUpPHvFuUplEC0URxVWSDHlWyU1xRttZnTJJCpR+paMolKsxLyA+8GJ7k6SJPagzweOR7//gsxxs1OAUEiPEz0NevXj2Xhm5GZVEFMqxQWkFH0Ywp6gDXS2Kw13VVY/O89/dI544vpa+tjaHdxrJ7o4ERlQ0lMwzm6TyXrxY5fbKsm3ypFE+GAL8dBBlPUAQFO/VSenEhhK/KqKB3m4KKEb40KpTlz5COwmI3qkKg3+Y64zfaHrWtepfKQoi2aOGEpdEDhrQu/dtSefWVsusJXYitWCkHP/tEjhZ+IYc++Via31ohu555WjbefYfSMIO0AxeUDB7LZ1/GEG40pLjxco0sc97G/igbM21r1uhUM1HHmcHppjxPDSs7LciiPAuOpDxMnyBH1q1jHxg3kCGkm4JtUUaqMIjLRgMjKtsrhoSpYnY/+YScWF8jHeXlcqysXI6UF5+VjlaUSEdZkbSuW0tPqVLhw0j8vAmcDK0Ob9XMfBnoPu14AGNGQxAef5TR/l6pmFOghqeGokZImuDRLq+cVyDtn/9Thjp7ZFC9aSihuGAosBLNEwldwff3yUDPGRk4c1xOrq+VL596QioKcqQ4bZIceO9tG99DoOP8ANxoRkp9exYjPmoWzifPlCWmD1UsjUuNE+sai4IqEzWEmusXWCfS4WhxGdAhOakE/KnCyMqGIJUYEAJlN61YxvuDanlJgnFA5iHcYdCq4RqeA6a4eFKGGdrJfKZ89eyvA+2Wxz3LBHfwH6ul9DJMI6ZcRpx8lLOk8qqZ0tVx2NFkXhDwcZvmFngOF5XPu3o52Kf8vs0yWrAfWTubv3MB2+PHDRjn//DaTyhDThVq3OC75DKNQmqsUHRFLnYGmLc1V96QHyoujmgDrghdNEb4SR1SmLPVAv3KtSBTGp2yUxUEAALGGvNEXS0VzqlBQxkWKJgeSvJypHbBPBns6XEdDLcJzSlI/+ofe9QMT+mphEdASBCeRo0jGq7JvCa0pxxs0rN6D77s2uEPW5hIQX47YzSMDIaDeP10g2r+DErt3bfRGGHgmLYgx0IYqPJRPkOnMVUup0g1CMzlaLP5ju9ZdyJ2q3OXrBAZ8mggJc+GQItduDnw1jIVDFpQSklwLo8mkEAUBuXg3z+U0nSd9/1qFMpW/JVzZkrHhvKAg5lD59FuWnynGYe293QhYRE22OcXNlCceTFBK4mKPy4BaAgmTN828iIVph90BPB7YS9+ju26HtFprmomollEK6edrAzZet89UnntVaZsTbgHL0cqm5UnHcU6dztaAHF6QjmqSglGXqCpUH1CKNq/8g03hhKBgk8xCEJjvRGLHDaaON0pZbOxUNH5WkM6QjjmrsqZOdKy6j22877l8VhIG5QNty2il2AqoPFxSlBv0IUghe3Ho7Jdfwf0YrTSOta6tlELAK6jOh8YLghnNXL9h/pk52MPU7lcnziFYgrC4vb4lk3S9NeVUjhlEnc3ZTM0SuVN5SIWaefDDyqmiA/gZIrxNFpILYyrojnv5GXzqBFgZFwYPGH265Te3SN19/yQhws2l9mKurwgV/Yvf81x5FagLgENlLD5/sVUtjc+ejeig26zOspKImHoHwwG1wH8BRs5+jWnajQseoOyZm5clkcA18iMiQXCsS1bpOa6OVQypi3QCuMsypomdXd9T/pVDoOnT0rpnFx3wKN7fng9jFdD+Yb5c+X4xo2Gjj9eHgY2THSdCqS2QHMJ2ySv7CTAAgh5GFsLWoE6JIrSqONKuO7O23VfOcmY0wTFlRSoIS0z3LY0c8gcHsDe53+vczVCv8532icIUXFUXT1bt02ngxF6pdtFlHkDhCqj2zZWdN8p2jcYEay9NdeyLvgaXnxBCnVbZ8ozb4VXF06+WNo/WaON1ZwT/dL42hs8vCrNtoMdW7RmyLrMCdLw3B9VGOAI+E2mEU2OxlHAmJVt4+vAKqRg3R4Q4kiRI1Sh9+RxKSsw7/R4EcoZxv/+gfVBc9cewKL+HK+rUaPIsZVrrC/CelF2ulTPv1KOVlZKQg3K94HpeL+NYJjgNI/a+zw1YYY2KLiLzuZmqf3uTTzcwQocYRzTFlLVrddJz0F7AIP5vbNpj1ReebkUz8DhkPKkvGDxWaTzOs7Ou/bvN0r0Lxiy+2E+Chi7Z0O/QZd+tlUYdMJS5Q0omSgjwXthyVAS51wqTBWlW6czX+5ED+0KpAbekDxs/vFdUpw50TxB+zIqYOFDhU/VlXm6bPvJvdL+r0/lzN4G6etxD15gjB6PM0JcMaHeRyfWO9r5kwKwD3L1VqX98D8/1rnY1js8HgV9WFhmpUnjS38OYkIfGObe3/5ayqYrT5Cxejb33lpelzZRDrz/rtJn7Q1M2Ml1qcE4hHH/Ok2ygPyca5cgMCGHPv+nbr1yqWh/gsSwpaG59vZb2DYwoQrxRSvoAm9wQLpaWqTm2nkaInHsaHtU4MCRaRH2rBo28ZChOHMyPWPX449K68oVcrRmo/SfPOXQmTEZWotI8TIiQRh7RHDC1xI8NdHbIxtuvUnKsjSEU25Kn9taVS2YKyc3b7G26Od4PFpRZt6tSgZPPpRDRhuuuyY8mOFYpM7A6lKH8ZmzFWy74wmIznWRug60yu5fPyGVuscG85i/wAxDMFbXWVPk8Cf/QIcYIyZEltUCAmNaOL5th6z/ztVSNH1KbCtmZ82WbP8Nbwdu1FUvmC2bvn+z1C99WNrXfCz9R3AAY/iCxyPDNYZmFei/MAQ60U9lcKykhHLDDoOHPzA+VSAUuX3pQ9pIOURbJxukod5+RiMeXDkejCeNDIqr7dOPbQgnYxsLNaODcVE2jiFPb97KveHR0jINoZ9I61/fla/+8Dupu/027hur8nWejq1KWVZFlU9Pk63/vVixOKEpWBaPCiwQ/LlaT2uTbL/3XinOQIQwoynSHMKlkHQ7g6PJpFDqQn3VFXlSOne27HjoATmxya14CeY1QRHucOdC4GlE+Ea/jbfdzPmXfGJxRoVPk8o5eYxsfrCgOI4j0rpmtUa9fNKIOd4WdmnEVTVvbmiL7oHeUEgNxkHZIDYhhz9bwwcL2FoUZ0/mo8dCtW4IGQ8AGKLyzaOhHO6X1ZLxML/7tIbXQLhOaFrGJeZ6Yy8SuvckhkxtgSPUqmvmkDZ7Gqe487O5neFJnSaERT6KdSESZQgTC6LCrMlSqwrq1K1SIhEL6aRhZGUDcHSM9h211XwGABo4BmTmotimGxdpE1s/GDg+XJ4Y6JGaW65X2nPZj8lHBd1atsFQXFumrwHjFsYxH+54+H7un7Hg8p7rwxHPw5VwKl/nMxwNQsjdDQ0Og4eYgCkMy5nRG7zHG6CMBx6tH62WusV3SfXCa6XocvVkXQzhsaEXOrdq4EFpYO6nEjU6PHrE07pdTzwmCZ6vG8THOT9E3rblR3cHHsOZN/jXxWTjqhUOYTL91J8Lyc3vrNL25iA+CoJW5DU3LHRTnO8fk1OKMD7KdtRjoVExd5YSiBcKXGjNtTBbNQM4VLBqpQjru59+WrraWq3jmACRIAq/XXt2S/N778quJ38utXfcIhUaPgvTJ3HRxgUc+FBF8y2afCjGLfTUiwozLtWF4q1yek+9oaZA48qJCxhackXNT2ytM09UnOXZWD9kkncYdeW8KyTRbd5PfJqHdYirAwz0dEvlVbO5MAvKdk4DpbetXesUbt3YdRQwLmGcYUwBg9f/6nEy7QVrIQ3PrnWfqXPlrheelRNV1XwEOR5Ar4hxjWsEf4TFnuZWPo49+Lf3pX7JQ1J5ea6smzaJ+18L5xbSscjDQwkYI/brOOHqbm0NaJ3jKZhSTFHeo60OCz9EL/BLJblUOu0S2fvKi2xrOxQHMZq5aEOm9w8sXybrMtU4sTWFzEGnOgrm8W0auayd27t5zacIY1c2iQYTtgLvOtQi1bqNqMrJ5RzKcKmeg+NAEH16/z50cBBj/muCDY9fCB9eE5OAbQl4B9sX0Nb03go+Di2aHp3g4bm6hUuUp0hh9iTZ/5c/cxtlKjUgbncZanWMnvovpfq6q0II94qCIWGBlThzOhgMsmSvxrXhxeVgV6dUXH4ZaeLUpznfpdO8Yt5MORJ7QOK6pwzjNmebhZvyYJ0R07YixioT5S0/upPtyPxoqR0BAjrixl452ZMgVAh6YKBPGl5/hcbH+VHp869LYVuIhxLlM/Okt7XFdfZg+IKYXbb3+T8yXPv9NPDRyKdNlD2//6014vjWwXXjtdkjoqMWcKF/oK1IpxQsMjk1YKrRxS3o3fX4I9aOMDpnGRdle18KFquw/rvXm1UqoZx3QDCEmZ/DV4TQzrcdKwRDU4Qeb4TbVMPkha0ZQuqxdYX0IioGxonnybpVwpYNC7ZW7P1VsL4fkHgemavx9DQ2ysYf3EpFkEd6N3hVntWru5ubtLXSBjyuHyDg9OAusfroOXpYKgt0e+jwwAD9Cx9VC+fJqbo6Zxf/D8oGeCHgp18XTGe2b7X3uFWAob/OO3h4X3PrDdJ7/GhgcEwwDEcQIrJYET/x6GPz5KA0vvoipxsIlQc8jEi6R9eF5Fe/fYorffZxuAhahglh7sTWr0hX/ugPo7b+yq/23/7LR2Sgq4fv9CT19zQogCZ/iyX89fbLvud+x9ebbaGmdOXZohevTe976QVJ9MeOplOEcVO2P8QLAlXT++pPf5CiqXbmy8WLhiQuiAqypPGVV7Xt6AkeDklztJMaMn9caynWzuRJo4B3nNqyWdZ/Z57SaB5koVPn2szJsvWn97tzek2uH4Feqqo5eUI233uPvV+HgxDF4U/BoHCcptlgLgHiZUCsDAq9sZ7cXCeVs66gs5gRqUFqKAd+HLt2tx7QVsZbqjAOCzRlWumjKCE9CMEJtlu3VjW3XOe+1DDvoVBwKrRwrpzevYftxgxOYEFuoaDAcqR0Qrg/KJ2NDVJ7001UDg1RFc6F1fR02foTKNuj8JHLG5dOAxtqpSzDPkLwHo25vyRrEt9E6e1oZ0v0oxNowcqWgIM55aZ1VDTqdA7v6ZH6x5fyAQmmFtAFHUDx2Eoe0O2ld7BUYdw8+3zQtvojhjlsvbjPVoVjLsIx565nntK9ZS/bmQD8YagpxpcpBP7FFAaBaA3bWEOfWSFcuKITqBNvABjchhsXkCY8K2eo1G1jSfpkqX90afhcCRDGBwrFV/fgYinNhIGoAWt/LvR0jw2lN67whygjgLZhs+G5ygKnZnhdi9FCo6ItJNWosDKfe4V7FStZJujr+1sewTeu7IFjx2Trg/cxHCFMhpCEpN5zctsm1xJg1k8IVJuls+TqBnvP8GiTFWyD+ugxKsBy9ItSmM8VKBb9a139ga68cSaNN0XApztjT58ojW/jfTv0cSaiP77fqX17pGTqFHocDATKQBmf9tTeuJAng6l4HvB73ManJhtChnTLVnvP9/l8G/LjOKBRx4J3t36wCj3DOAEX8SjYRYBvXNkY+EhJsVTMuYwewzCpRPPhRNYU2fSD29wBi52JewKRORE7cPO7Knjbkp/K5sU/kJObNkmC5+qRMqxXdMgTz1kwWfKnu6mFz8dxtMp9Lfm0s3R86NC5cztaKhh+w2OCrH/kf6R0GpRtL2KY0jOkKD9Ldv7vY9o49RBreBVYMPx4BgMMTe++w4dIwI0xOE1A6ToVrr92nraAXJxyA3gmk+E/omyMu/MXeA0Y2xJdtfK0ynDiqxLMP0ZbFMZp5XGanfAOrl3Lr0sxp2L1vP1nD8nBT1fLqT27+H6bWbdjXjtzEeiQ0Ev1D5/jHKspl233/0gVpiHRnfbBGBF9sBLf+uMf2tchANDhPBzQ2dqo40+goqkEDbP4LAqKqJiVL6dhJBzT0ZECsDl+NIWIgPKZLqn6znwuzrDI9esDbg8zp+o0+aG1jfFsZecAMfjmle0Y6DvYKuVXXU6PrnR7WRANvHi0d+YAVpcGJlgULLkr6W1vta9LVCEIZVAS3hnHe+0bbr5etvzsQWl4/jlp+fADOVJVKad2bJOuvV/RELDqbv/3Wml6/VXZ9tAD/ESoSNcN5pH2SBLKK8LLfwU50vbF2iAumiCLVrPr50spdH8k7Ptjj41vwtmUXUanbPYjmLGzv1Y2vPqyvaCZm2NRMbaQxJMytPR9Qz+3RonDN65ss1ELwfiIryRTV5dKZJHigrK5D1dhbX/gxyQYjzW9UIeTu+ORpUoHrFsXQ3nan49M3ZSggsbRJHDic6LK+TP5EiI+qFt/7dXcrpTNLbA+OiYehKCfP7iw13lta/PVb56WxEBfTAMRPd3NDVI1b1YkE7d4QhkPW47V4jtv13hU4MbwAzlAMdHbxecKkJs/YMF4KFcUzJC2T6MPJOLrhBgawn8sjPvFw6abb+TDBjsswFeMbtExJ1c6PvUP9+Meof2174n6nVJ1zUxVzlQpxGmSC53eq3gs63D5iIE6TB0o+3tI/HISC0auvo0Wfr6r7XcseVDHM4PDrwnM0zPIswM8L+cCE+NwfIsINbfgmbW1sy5xPs4D1iGM6S6t3nfX8t4XnzNHUQM1XhyvKgds8/ANWzhb8EgCMoP/gLIBRjXG7ty1k1sxeJbfRtDDta5u8T3Sf/x4aEvmVdFe6Phktf7Jx6RKtx3l+XjL1DwqnixaOAW7OlvYoBwJCULDUy9/H69M7fnTbzgqBuP4RgRzxKYeHR/P4OHNfM87GJriSpskbfze3K07fN8RwIfqUHZg4RhgCkx0nmEIp3OowXNtgcgE+q+eJe3lRWztcZ0LVNluMCa8/QHAv9lQZU+daJ+XIlxowrvdTW+9yRYRISOBKcwzgvLePzzDhRmOAwszJvPQHw8NMIc2LH/JrNwIIeDaH9Rg4N6WA7L7lb/IlrvvVG+fzZVpueLBWyeIGt4IoHgvEHqvKgmGxpcnMNdrfc3182X70iW6BdyipCpyMjacL/PyA++tlEK8EJExhe+/FWZMJA/Fad/SqeIa9S5bGYNH5ug6DmAkDcn+vzwv/57wLX7uhE+AkSi3yf8lOx/9ufTpljTAOQY/6/+gGQzqoqaer7Ee/PBvmj5gwp70zO56IjKGRoZkyzXoPtEuTW8u49Ox5mWvS+uy1/T6DWnWxdPhzz+ToW7dR2s/P/+gH8eL41HlD3SekhMbanQ//JbsfupJvstWc9NCbvPwAkChCgGCwIv563QrhUeF1ddcJbV33SE7fvW4HFz1lq6cdyo+W1MAsZWgKKPbVKa1iUE5WlqiNL7M/23SvPxVOfDm63w1GguoIzWVVEiSXGLFsYFR0d3eIvtfeUnl9obS8DrH37fyVWl882VpXfOxbUMVzj5OMTBl6w+IjBKEHFeSDZZsq9H9kYALQ7ZVDF4YmiVtiywzwIVP5xgnhDj9sVtqFrqIOdPeJl37GlWBX8rxLXV8mfDEhmpLWob3du7ZLf2H2jQsdkeOrBBXUlTnCgqxYhgzAl/2tHozHTsA3QDODTCEXkQR0nQU6KKQ4zSdDdGczcYGXpiscdU0AisS/KCpQkScJvUSE64jHHVOsjaylVDEVVCE1467BHi6Aj2xe348gquPbqPejT+Mt2irBWM0OtAWHOAhWKAdmbvnhc2HZA6v3Rs7DInuDECLH5Mlj1wj4ICn3zmPJn83Dvynd+7+MADBUIK/H2MgNHZ1FwK2NVwBiDPe1+5HDPj7ro3D4e8G3bsaCj/WJoQx7e7bAkI7V2fFiA6Wk4NayAHROBjBxvIJ3SKjAc44N2MAP4AaE8ZAmXiHI0e9G99u6fhx5hW4QIsaAIxQQKjzBeZor/djArkQcDugDW0UN5YnwmfDiLJGJkCjJRrM3dI+ppVgFP4GIJRxD23j3m91AFLj+rgsVojaAYxGG49l+4vaOxL8ta8eK8TDdZxXw2/Xw/niNfslQ5izXW+FqBOAzLkywK6T24wISbg9PhCkWRx5DFitP+dsMqwifhlmI5efZUhxiN9CWVOgjpnRaJEC5UiAHi1loY2thSnGofDZGEEpivGQhBMXwwdRWsAD6Uq6J/J/UVbWOhNKgAwAAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"a02167b9-ae71-4ac7-9a07-06432ebb6f1c\": {\n    \"name\": \"YubiKey 5 Series with Lightning\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"2c0df832-92de-4be1-8412-88a8f074df4a\": {\n    \"name\": \"Feitian FIDO Smart Card\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"59f85fe7-faa5-4c92-9f52-697b9d4d5473\": {\n    \"name\": \"RSA Authenticator 4 for Android\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"970c8d9c-19d2-46af-aa32-3f448db49e35\": {\n    \"name\": \"WinMagic FIDO Eazy - TPM\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAMAAAAKE/YAAAAB1FBMVEUAAAD///8RBfcSCfMSCvITC/ETC/ATDO8TDe4VEukWE+gXFOgXFeYAAM8AAM4YF+UaHOAAB88ABM4BB88BCM8CCc8ECc8ID9AaHt0bH9wbINwbINsbIdocIdwcItocI9kqMNcBC9ADC9AEDdAFEdENF9MNF9IPGNMPGNIPGdIRGtMRG9MTHdMUHtMVH9QVHtMWH9MWINMXINQYIdQZItQaI9QaJNQbJdUbJNQcJNccJdUdJdcdJtUeJtcdJtQeJ9UeKNUeJ9QeKNMeKdMfKNUfKdQfKdMfKtIgKdYgKtYgKdUhKtYhK9UiK9YiLNYjLdcjLNYkLNYkLdYnMNcnL9YpMtcqM9gsNNguNtgxOdkxOtlFTNxNVN5RV95TWd9VW99dY+FkauJiaN54feaFiuOEieKFiuKGiuOGi+OssOgnPr8rSLUtTq87cI09dYlEhndJk2pKlWlMm2JNnWBNnWFVsU5VsktWs0xWskxXtUpWtEpWs0pWtEtWs0tYt0hWtUhXtUhXtklXtUlYuUZYuEVYuEZavUJZu0Ravj9avUFbwD5bvz9eyDhexzldxjldxTlfyTVeyDZexzdexzhexjhfyjNj1Spj0yti0ixi0i1l1yhk1ikVqiEiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA3LTIxVDE4OjE0OjA0KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjIiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowYjEwNjE2Yy0xOWE0LWU0NDYtOTBlZS03NzAzM2FkMGQzYWUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5N2M4NGE2Ny03ZDJlLTBlNDctYjAzNS1lN2U4NWIxZDk0ZTYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMmUxNGRkZC05ZjAzLThkNGItYTc2Ni01MmE4MjhjMDdhNjciPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIyZTE0ZGRkLTlmMDMtOGQ0Yi1hNzY2LTUyYTgyOGMwN2E2NyIgc3RFdnQ6d2hlbj0iMjAyMC0wNy0yMVQxODoxNDowNCswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZl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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAMAAAAKE/YAAAAB1FBMVEUAAAD///8RBfcSCfMSCvITC/ETC/ATDO8TDe4VEukWE+gXFOgXFeYAAM8AAM4YF+UaHOAAB88ABM4BB88BCM8CCc8ECc8ID9AaHt0bH9wbINwbINsbIdocIdwcItocI9kqMNcBC9ADC9AEDdAFEdENF9MNF9IPGNMPGNIPGdIRGtMRG9MTHdMUHtMVH9QVHtMWH9MWINMXINQYIdQZItQaI9QaJNQbJdUbJNQcJNccJdUdJdcdJtUeJtcdJtQeJ9UeKNUeJ9QeKNMeKdMfKNUfKdQfKdMfKtIgKdYgKtYgKdUhKtYhK9UiK9YiLNYjLdcjLNYkLNYkLdYnMNcnL9YpMtcqM9gsNNguNtgxOdkxOtlFTNxNVN5RV95TWd9VW99dY+FkauJiaN54feaFiuOEieKFiuKGiuOGi+OssOgnPr8rSLUtTq87cI09dYlEhndJk2pKlWlMm2JNnWBNnWFVsU5VsktWs0xWskxXtUpWtEpWs0pWtEtWs0tYt0hWtUhXtUhXtklXtUlYuUZYuEVYuEZavUJZu0Ravj9avUFbwD5bvz9eyDhexzldxjldxTlfyTVeyDZexzdexzhexjhfyjNj1Spj0yti0ixi0i1l1yhk1ikVqiEiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA3LTIxVDE4OjE0OjA0KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjIiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowYjEwNjE2Yy0xOWE0LWU0NDYtOTBlZS03NzAzM2FkMGQzYWUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5N2M4NGE2Ny03ZDJlLTBlNDctYjAzNS1lN2U4NWIxZDk0ZTYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMmUxNGRkZC05ZjAzLThkNGItYTc2Ni01MmE4MjhjMDdhNjciPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIyZTE0ZGRkLTlmMDMtOGQ0Yi1hNzY2LTUyYTgyOGMwN2E2NyIgc3RFdnQ6d2hlbj0iMjAyMC0wNy0yMVQxODoxNDowNCswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZl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\"\n  },\n  \"c5703116-972b-4851-a3e7-ae1259843399\": {\n    \"name\": \"NEOWAVE Badgeo FIDO2\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAACqUlEQVRIx2P8//8/Ay0BEwONwagFpFlw8cKFirIyR3t7S1Oz0KDgBfPm//z5k3izvn39lp+Ta2tltWTRIoTofxhYtXKllpq6srwCAikoRIVHvH379j9x4NSpU0AtQI1W5hZwQagPzp87V11ZiXAvIxj9Zzh54kRNZRWRPvj96xcDOM0zMTKiB9G8uXP//fsHNFRASLC+sXHm7Nlubu4Qm3bt3Llu7VpiLGCEmcuIacGZU6fB4cWQX1AQGx/n7OIyaeoUbV0diIvamluePXtGUST/+g32HSODhoYGRISFhaWppYWVlRUo+OHjh6b6BoosgHvqz58/cDl9ff3M7CwIe8+e3atXrqQgmeIokDKzs/X19EGy/xk6OzofP3pEWUbDsAYYRC3tbRwcHED2h/fv62pqCReOjCTmZE0trZy8XAj78KFDy5YuJd50VAsYcepKTU83NjWBqOnu7Hxw/wE+O/7jsgC315mZmRubm9nZ2YFqvnz+0lBfhzOg/qO7lQm/B+EAmHwLioogCo4cOrxk0WIiPUEgkpFBUnKymZk5hN3T1XX3zh1iYoKJcDTBA4qFubmtlYubC8j++vVrTVU1qHQhzQeMBHyhrKxcWFwMUXn61Kn5c+dSv8JJSEy0trGGsCf099+6dQsuxcLCCrH7P5IrSYgDeKFS39TEx8sHZH//9r2uGhFQN65fh2VPNoqqTCUlpeKyUmgxfPpMSWERMAMuX7asv7cXIqilrYXwFrxeg/qOuGZSdEzM3t17Dh06CPT0pk0bN23cCI9FYKZJz8hE98Hff38hDDY2diL90dHdpaurixawrCysre3tunq6iLTX0NAAToIsTx4/tndwiIyOAtYExFjAzc3t4+sLJL99/QosE0VFRe3s7RtbmoGVFUqcjTYdh78FAIhBLlNd7ju1AAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAACqUlEQVRIx2P8//8/Ay0BEwONwagFpFlw8cKFirIyR3t7S1Oz0KDgBfPm//z5k3izvn39lp+Ta2tltWTRIoTofxhYtXKllpq6srwCAikoRIVHvH379j9x4NSpU0AtQI1W5hZwQagPzp87V11ZiXAvIxj9Zzh54kRNZRWRPvj96xcDOM0zMTKiB9G8uXP//fsHNFRASLC+sXHm7Nlubu4Qm3bt3Llu7VpiLGCEmcuIacGZU6fB4cWQX1AQGx/n7OIyaeoUbV0diIvamluePXtGUST/+g32HSODhoYGRISFhaWppYWVlRUo+OHjh6b6BoosgHvqz58/cDl9ff3M7CwIe8+e3atXrqQgmeIokDKzs/X19EGy/xk6OzofP3pEWUbDsAYYRC3tbRwcHED2h/fv62pqCReOjCTmZE0trZy8XAj78KFDy5YuJd50VAsYcepKTU83NjWBqOnu7Hxw/wE+O/7jsgC315mZmRubm9nZ2YFqvnz+0lBfhzOg/qO7lQm/B+EAmHwLioogCo4cOrxk0WIiPUEgkpFBUnKymZk5hN3T1XX3zh1iYoKJcDTBA4qFubmtlYubC8j++vVrTVU1qHQhzQeMBHyhrKxcWFwMUXn61Kn5c+dSv8JJSEy0trGGsCf099+6dQsuxcLCCrH7P5IrSYgDeKFS39TEx8sHZH//9r2uGhFQN65fh2VPNoqqTCUlpeKyUmgxfPpMSWERMAMuX7asv7cXIqilrYXwFrxeg/qOuGZSdEzM3t17Dh06CPT0pk0bN23cCI9FYKZJz8hE98Hff38hDDY2diL90dHdpaurixawrCysre3tunq6iLTX0NAAToIsTx4/tndwiIyOAtYExFjAzc3t4+sLJL99/QosE0VFRe3s7RtbmoGVFUqcjTYdh78FAIhBLlNd7ju1AAAAAElFTkSuQmCC\"\n  },\n  \"c80dbd9a-533f-4a17-b941-1a2f1c7cedff\": {\n    \"name\": \"HID Crescendo C3000\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"5b0e46ba-db02-44ac-b979-ca9b84f5e335\": {\n    \"name\": \"YubiKey 5 FIPS Series with Lightning Preview\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"2a55aee6-27cb-42c0-bc6e-04efe999e88a\": {\n    \"name\": \"HID Crescendo 4000\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"820d89ed-d65a-409e-85cb-f73f0578f82a\": {\n    \"name\": \"IDmelon iOS Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEUtmc3y+fyWzOZis9rK5fI6n9B8v+Cw2ezl8vlHptNVrNbX7Paj0ulvud293++JxuP///89HRvpAAAAEXRSTlP/////////////////////ACWtmWIAAABsSURBVHgBxdPBCoAwDIPh/yDise//tIIQCZo6RNGdtuWDstFSg/UOgMiADQBJ6J4iCwS4BgzBuEQHCoFa+mdM+qijsDMVhBfdoRFaAL4nAe6AeghODYPnsaNyLuAqg5AHwO9AYu5BmqEPhncFmecvM5KKQHMAAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEUtmc3y+fyWzOZis9rK5fI6n9B8v+Cw2ezl8vlHptNVrNbX7Paj0ulvud293++JxuP///89HRvpAAAAEXRSTlP/////////////////////ACWtmWIAAABsSURBVHgBxdPBCoAwDIPh/yDise//tIIQCZo6RNGdtuWDstFSg/UOgMiADQBJ6J4iCwS4BgzBuEQHCoFa+mdM+qijsDMVhBfdoRFaAL4nAe6AeghODYPnsaNyLuAqg5AHwO9AYu5BmqEPhncFmecvM5KKQHMAAAAASUVORK5CYII=\"\n  },\n  \"3124e301-f14e-4e38-876d-fbeeb090e7bf\": {\n    \"name\": \"YubiKey 5 Series with Lightning Preview\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"b6ede29c-3772-412c-8a78-539c1f4c62d2\": {\n    \"name\": \"Feitian BioPass FIDO2 Plus Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"85203421-48f9-4355-9bc8-8a53846e5083\": {\n    \"name\": \"YubiKey 5 FIPS Series with Lightning\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"d821a7d4-e97c-4cb6-bd82-4237731fd4be\": {\n    \"name\": \"Hyper FIDO Bio Security Key\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"9876631b-d4a0-427f-5773-0ec71c9e0279\": {\n    \"name\": \"Somu Secp256R1 FIDO2 CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"f56f58b3-d711-4afc-ba7d-6ac05f88cb19\": {\n    \"name\": \"WinMagic FIDO Eazy - Phone\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAMAAAAKE/YAAAAB1FBMVEUAAAD///8RBfcSCfMSCvITC/ETC/ATDO8TDe4VEukWE+gXFOgXFeYAAM8AAM4YF+UaHOAAB88ABM4BB88BCM8CCc8ECc8ID9AaHt0bH9wbINwbINsbIdocIdwcItocI9kqMNcBC9ADC9AEDdAFEdENF9MNF9IPGNMPGNIPGdIRGtMRG9MTHdMUHtMVH9QVHtMWH9MWINMXINQYIdQZItQaI9QaJNQbJdUbJNQcJNccJdUdJdcdJtUeJtcdJtQeJ9UeKNUeJ9QeKNMeKdMfKNUfKdQfKdMfKtIgKdYgKtYgKdUhKtYhK9UiK9YiLNYjLdcjLNYkLNYkLdYnMNcnL9YpMtcqM9gsNNguNtgxOdkxOtlFTNxNVN5RV95TWd9VW99dY+FkauJiaN54feaFiuOEieKFiuKGiuOGi+OssOgnPr8rSLUtTq87cI09dYlEhndJk2pKlWlMm2JNnWBNnWFVsU5VsktWs0xWskxXtUpWtEpWs0pWtEtWs0tYt0hWtUhXtUhXtklXtUlYuUZYuEVYuEZavUJZu0Ravj9avUFbwD5bvz9eyDhexzldxjldxTlfyTVeyDZexzdexzhexjhfyjNj1Spj0yti0ixi0i1l1yhk1ikVqiEiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA3LTIxVDE4OjE0OjA0KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjIiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowYjEwNjE2Yy0xOWE0LWU0NDYtOTBlZS03NzAzM2FkMGQzYWUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5N2M4NGE2Ny03ZDJlLTBlNDctYjAzNS1lN2U4NWIxZDk0ZTYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMmUxNGRkZC05ZjAzLThkNGItYTc2Ni01MmE4MjhjMDdhNjciPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIyZTE0ZGRkLTlmMDMtOGQ0Yi1hNzY2LTUyYTgyOGMwN2E2NyIgc3RFdnQ6d2hlbj0iMjAyMC0wNy0yMVQxODoxNDowNCswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZl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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAMAAAAKE/YAAAAB1FBMVEUAAAD///8RBfcSCfMSCvITC/ETC/ATDO8TDe4VEukWE+gXFOgXFeYAAM8AAM4YF+UaHOAAB88ABM4BB88BCM8CCc8ECc8ID9AaHt0bH9wbINwbINsbIdocIdwcItocI9kqMNcBC9ADC9AEDdAFEdENF9MNF9IPGNMPGNIPGdIRGtMRG9MTHdMUHtMVH9QVHtMWH9MWINMXINQYIdQZItQaI9QaJNQbJdUbJNQcJNccJdUdJdcdJtUeJtcdJtQeJ9UeKNUeJ9QeKNMeKdMfKNUfKdQfKdMfKtIgKdYgKtYgKdUhKtYhK9UiK9YiLNYjLdcjLNYkLNYkLdYnMNcnL9YpMtcqM9gsNNguNtgxOdkxOtlFTNxNVN5RV95TWd9VW99dY+FkauJiaN54feaFiuOEieKFiuKGiuOGi+OssOgnPr8rSLUtTq87cI09dYlEhndJk2pKlWlMm2JNnWBNnWFVsU5VsktWs0xWskxXtUpWtEpWs0pWtEtWs0tYt0hWtUhXtUhXtklXtUlYuUZYuEVYuEZavUJZu0Ravj9avUFbwD5bvz9eyDhexzldxjldxTlfyTVeyDZexzdexzhexjhfyjNj1Spj0yti0ixi0i1l1yhk1ikVqiEiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA3LTIxVDE4OjE0OjA0KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjIiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowYjEwNjE2Yy0xOWE0LWU0NDYtOTBlZS03NzAzM2FkMGQzYWUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5N2M4NGE2Ny03ZDJlLTBlNDctYjAzNS1lN2U4NWIxZDk0ZTYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMmUxNGRkZC05ZjAzLThkNGItYTc2Ni01MmE4MjhjMDdhNjciPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIyZTE0ZGRkLTlmMDMtOGQ0Yi1hNzY2LTUyYTgyOGMwN2E2NyIgc3RFdnQ6d2hlbj0iMjAyMC0wNy0yMVQxODoxNDowNCswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZl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\"\n  },\n  \"f4c63eff-d26c-4248-801c-3736c7eaa93a\": {\n    \"name\": \"FIDO KeyPass S3\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"d384db22-4d50-ebde-2eac-5765cf1e2a44\": {\n    \"name\": \"Excelsecu eSecu FIDO2 Fingerprint Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"b93fd961-f2e6-462f-b122-82002247de78\": {\n    \"name\": \"Android Authenticator with SafetyNet Attestation\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB7klEQVR4AaWPP2sUQRiHn5mdvexd/plEcvlDCi1E/EMabUWI9jaKWPoV/A7BQhAbG7t8CCUIKQQLuwhCUBsLBSUmGkLudm9n5nWHzMAego3P8Oy9s8vvfd+jzctPz2Ya+Zdbu48mG0ma8Eh8/bF3yWGGwPvV81d7+9/2lpy3Mrty7jswPPz8Yb20lQJ2iain2w9ok02aLURWstxuiHgknnrEK3GERg9poZ7s3CUxl/dvVfrntmRag9BuICJgrXfHnRvAWyJaDxXB+ezCWqX3t6e6i/ri/E1AkdBoLi/cZrL5pqeHb2yvu9RIUKfiWH95IVmmV6eucK1/j8JMIwRo6jNcX77P2vQ6ZEZ7OXreSFA93rnD3Mx6r7YfTxQKGkN4WP8eW7+bz4Z3eHEE9FFZAJXuliXVyUEfif9ZHINW+BQ5fSc+3oTjztTZRkx4LEhtfh1avBMSIkBrA+JvOAohm1AFgJGRpbOoXS/X1KXgHZE4X1Ssxpt18iYImGJiRFWWKCXkBdiR4L0QUEKamIKxhoQZm6fAdMDVjT7cQwBEYh3DSsl4A+trQTwJbUCsT5P+CodTZtYDmNJYcrEDQSChIMsVzoVQ2kLFMCCQFW4AoDbfbRDI7fIi5aAL41jtVNiQiPUjmUBOgAMCm683/ss/TaVXtx4qKMoAAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB7klEQVR4AaWPP2sUQRiHn5mdvexd/plEcvlDCi1E/EMabUWI9jaKWPoV/A7BQhAbG7t8CCUIKQQLuwhCUBsLBSUmGkLudm9n5nWHzMAego3P8Oy9s8vvfd+jzctPz2Ya+Zdbu48mG0ma8Eh8/bF3yWGGwPvV81d7+9/2lpy3Mrty7jswPPz8Yb20lQJ2iain2w9ok02aLURWstxuiHgknnrEK3GERg9poZ7s3CUxl/dvVfrntmRag9BuICJgrXfHnRvAWyJaDxXB+ezCWqX3t6e6i/ri/E1AkdBoLi/cZrL5pqeHb2yvu9RIUKfiWH95IVmmV6eucK1/j8JMIwRo6jNcX77P2vQ6ZEZ7OXreSFA93rnD3Mx6r7YfTxQKGkN4WP8eW7+bz4Z3eHEE9FFZAJXuliXVyUEfif9ZHINW+BQ5fSc+3oTjztTZRkx4LEhtfh1avBMSIkBrA+JvOAohm1AFgJGRpbOoXS/X1KXgHZE4X1Ssxpt18iYImGJiRFWWKCXkBdiR4L0QUEKamIKxhoQZm6fAdMDVjT7cQwBEYh3DSsl4A+trQTwJbUCsT5P+CodTZtYDmNJYcrEDQSChIMsVzoVQ2kLFMCCQFW4AoDbfbRDI7fIi5aAL41jtVNiQiPUjmUBOgAMCm683/ss/TaVXtx4qKMoAAAAASUVORK5CYII=\"\n  },\n  \"2fc0579f-8113-47ea-b116-bb5a8db9202a\": {\n    \"name\": \"YubiKey 5 Series with NFC\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"31c3f7ff-bf15-4327-83ec-9336abcbcd34\": {\n    \"name\": \"WinMagic FIDO Eazy - Software\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAMAAAAKE/YAAAAB1FBMVEUAAAD///8RBfcSCfMSCvITC/ETC/ATDO8TDe4VEukWE+gXFOgXFeYAAM8AAM4YF+UaHOAAB88ABM4BB88BCM8CCc8ECc8ID9AaHt0bH9wbINwbINsbIdocIdwcItocI9kqMNcBC9ADC9AEDdAFEdENF9MNF9IPGNMPGNIPGdIRGtMRG9MTHdMUHtMVH9QVHtMWH9MWINMXINQYIdQZItQaI9QaJNQbJdUbJNQcJNccJdUdJdcdJtUeJtcdJtQeJ9UeKNUeJ9QeKNMeKdMfKNUfKdQfKdMfKtIgKdYgKtYgKdUhKtYhK9UiK9YiLNYjLdcjLNYkLNYkLdYnMNcnL9YpMtcqM9gsNNguNtgxOdkxOtlFTNxNVN5RV95TWd9VW99dY+FkauJiaN54feaFiuOEieKFiuKGiuOGi+OssOgnPr8rSLUtTq87cI09dYlEhndJk2pKlWlMm2JNnWBNnWFVsU5VsktWs0xWskxXtUpWtEpWs0pWtEtWs0tYt0hWtUhXtUhXtklXtUlYuUZYuEVYuEZavUJZu0Ravj9avUFbwD5bvz9eyDhexzldxjldxTlfyTVeyDZexzdexzhexjhfyjNj1Spj0yti0ixi0i1l1yhk1ikVqiEiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA3LTIxVDE4OjE0OjA0KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjIiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowYjEwNjE2Yy0xOWE0LWU0NDYtOTBlZS03NzAzM2FkMGQzYWUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5N2M4NGE2Ny03ZDJlLTBlNDctYjAzNS1lN2U4NWIxZDk0ZTYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMmUxNGRkZC05ZjAzLThkNGItYTc2Ni01MmE4MjhjMDdhNjciPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIyZTE0ZGRkLTlmMDMtOGQ0Yi1hNzY2LTUyYTgyOGMwN2E2NyIgc3RFdnQ6d2hlbj0iMjAyMC0wNy0yMVQxODoxNDowNCswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZl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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAMAAAAKE/YAAAAB1FBMVEUAAAD///8RBfcSCfMSCvITC/ETC/ATDO8TDe4VEukWE+gXFOgXFeYAAM8AAM4YF+UaHOAAB88ABM4BB88BCM8CCc8ECc8ID9AaHt0bH9wbINwbINsbIdocIdwcItocI9kqMNcBC9ADC9AEDdAFEdENF9MNF9IPGNMPGNIPGdIRGtMRG9MTHdMUHtMVH9QVHtMWH9MWINMXINQYIdQZItQaI9QaJNQbJdUbJNQcJNccJdUdJdcdJtUeJtcdJtQeJ9UeKNUeJ9QeKNMeKdMfKNUfKdQfKdMfKtIgKdYgKtYgKdUhKtYhK9UiK9YiLNYjLdcjLNYkLNYkLdYnMNcnL9YpMtcqM9gsNNguNtgxOdkxOtlFTNxNVN5RV95TWd9VW99dY+FkauJiaN54feaFiuOEieKFiuKGiuOGi+OssOgnPr8rSLUtTq87cI09dYlEhndJk2pKlWlMm2JNnWBNnWFVsU5VsktWs0xWskxXtUpWtEpWs0pWtEtWs0tYt0hWtUhXtUhXtklXtUlYuUZYuEVYuEZavUJZu0Ravj9avUFbwD5bvz9eyDhexzldxjldxTlfyTVeyDZexzdexzhexjhfyjNj1Spj0yti0ixi0i1l1yhk1ikVqiEiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA3LTIxVDE4OjE0OjA0KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wOC0zMVQxNjoxODoxNCswMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjIiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowYjEwNjE2Yy0xOWE0LWU0NDYtOTBlZS03NzAzM2FkMGQzYWUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5N2M4NGE2Ny03ZDJlLTBlNDctYjAzNS1lN2U4NWIxZDk0ZTYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMmUxNGRkZC05ZjAzLThkNGItYTc2Ni01MmE4MjhjMDdhNjciPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIyZTE0ZGRkLTlmMDMtOGQ0Yi1hNzY2LTUyYTgyOGMwN2E2NyIgc3RFdnQ6d2hlbj0iMjAyMC0wNy0yMVQxODoxNDowNCswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZl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\"\n  },\n  \"9ddd1817-af5a-4672-a2b9-3e3dd95000a9\": {\n    \"name\": \"Windows Hello\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMDc4ZDQ7c3Ryb2tlLXdpZHRoOjBweDt9PC9zdHlsZT48L2RlZnM+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIyNC4yNSIgeT0iMjQuMjUiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTMzLjQiIHk9IjI0LjI1IiB3aWR0aD0iOTguMzUiIGhlaWdodD0iOTguMzUiLz48cmVjdCBjbGFzcz0iY2xzLTEiIHg9IjI0LjI1IiB5PSIxMzMuNCIgd2lkdGg9Ijk4LjM1IiBoZWlnaHQ9Ijk4LjM1Ii8+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxMzMuNCIgeT0iMTMzLjQiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjwvc3ZnPg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMDc4ZDQ7c3Ryb2tlLXdpZHRoOjBweDt9PC9zdHlsZT48L2RlZnM+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIyNC4yNSIgeT0iMjQuMjUiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTMzLjQiIHk9IjI0LjI1IiB3aWR0aD0iOTguMzUiIGhlaWdodD0iOTguMzUiLz48cmVjdCBjbGFzcz0iY2xzLTEiIHg9IjI0LjI1IiB5PSIxMzMuNCIgd2lkdGg9Ijk4LjM1IiBoZWlnaHQ9Ijk4LjM1Ii8+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxMzMuNCIgeT0iMTMzLjQiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjwvc3ZnPg==\"\n  },\n  \"d8522d9f-575b-4866-88a9-ba99fa02f35b\": {\n    \"name\": \"YubiKey Bio Series\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"50a45b0c-80e7-f944-bf29-f552bfa2e048\": {\n    \"name\": \"ACS FIDO Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"f7c558a0-f465-11e8-b568-0800200c9a66\": {\n    \"name\": \"KONAI Secp256R1 FIDO2 Conformance Testing CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"3f59672f-20aa-4afe-b6f4-7e5e916b6d98\": {\n    \"name\": \"Arculus FIDO 2.1 Key Card [P71]\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoCAYAAABNo9TkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAD6KADAAQAAAABAAAD6AAAAADrEeKkAAAACXBIWXMAAAsTAAALEwEAmpwYAAACzGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cl9EK38AAEAASURBVHgB7N1/jGVZQh/2e+6r7pnp39VdPT1dVd0zuwwLw9iE0PxY2yRuSIRDLLBj5MgEQgw4/iGwHAKJI5wfsmXFimUlVmJHSpRETkikSLEi5a9EimNGOJEcdoddkNdr0AJDdjzs7A4sC7sz01317sk5577qqf5dVe/X/fF5UF2v3rv33HM+p7aqvnPOPaeqPAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwIoEwoqu4zIECBAgQIBAvwXy3wz1rAkxfW763Ry1J0CAAAECBAgQIECAAAEC/RPwH/T712dqTIAAAQI9FPALt4edpsoECBAgQGCFAnnUvHn+xo2vmjbNX6pCeCb98fDL77z55l9eYR1cigABAgQIjEJgYxSt1EgCBAgQIEDgpAIloO+H6YfryeSHQghV08RPpcIE9JOKOo8AAQIECDxGQEB/DIyXCRAgQIAAgQ8E6jjZirGp8s3nIdS//cE7nhEgQIAAAQKLEjhY7GVR5SmHAAECBAgQGKBAUzXX8+h5SuepddF/4B9gH2sSAQIECKxfQEBffx+oAQECBAgQ6LxAHcLFNpx3vqoqSIAAAQIEeisgoPe261ScAAECBAisTiDNbr9YxTzB3YMAAQIECBBYloCAvixZ5RIgQIAAgWEIlP3OQ4jXhtEcrSBAgAABAt0VENC72zdqRoAAAQIEuiBQhs3T4PkLXaiMOhAgQIAAgSELCOhD7l1tI0CAAAEC8wvkgJ5uQQ/nywru85enBAIECBAgQOAxAgL6Y2C8TIAAAQIECFR5yfZqd3f3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gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCLRX4H8D7duTS/D4+v0AAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoCAYAAABNo9TkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAD6KADAAQAAAABAAAD6AAAAADrEeKkAAAACXBIWXMAAAsTAAALEwEAmpwYAAACzGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4zMDAwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cl9EK38AAEAASURBVHgB7N1/jGVZQh/2e+6r7pnp39VdPT1dVd0zuwwLw9iE0PxY2yRuSIRDLLBj5MgEQgw4/iGwHAKJI5wfsmXFimUlVmJHSpRETkikSLEi5a9EimNGOJEcdoddkNdr0AJDdjzs7A4sC7sz01317sk5577qqf5dVe/X/fF5UF2v3rv33HM+p7aqvnPOPaeqPAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwIoEwoqu4zIECBAgQIBAvwXy3wz1rAkxfW763Ry1J0CAAAECBAgQIECAAAEC/RPwH/T712dqTIAAAQI9FPALt4edpsoECBAgQGCFAnnUvHn+xo2vmjbNX6pCeCb98fDL77z55l9eYR1cigABAgQIjEJgYxSt1EgCBAgQIEDgpAIloO+H6YfryeSHQghV08RPpcIE9JOKOo8AAQIECDxGQEB/DIyXCRAgQIAAgQ8E6jjZirGp8s3nIdS//cE7nhEgQIAAAQKLEjhY7GVR5SmHAAECBAgQGKBAUzXX8+h5SuepddF/4B9gH2sSAQIECKxfQEBffx+oAQECBAgQ6LxAHcLFNpx3vqoqSIAAAQIEeisgoPe261ScAAECBAisTiDNbr9YxTzB3YMAAQIECBBYloCAvixZ5RIgQIAAgWEIlP3OQ4jXhtEcrSBAgAABAt0VENC72zdqRoAAAQIEuiBQhs3T4PkLXaiMOhAgQIAAgSELCOhD7l1tI0CAAAEC8wvkgJ5uQQ/nywru85enBAIECBAgQOAxAgL6Y2C8TIAAAQIECFR5yfZqd3f3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gAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCLRX4H8D7duTS/D4+v0AAAAASUVORK5CYII=\"\n  },\n  \"42b4fb4a-2866-43b2-9bf7-6c6669c2e5d3\": {\n    \"name\": \"Google Titan Security Key v2\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAD1ElEQVR4AeyXU5gcTRSG62L927b/aJLpnt241zGmd2Pbxl1s29Yotm3bdqY3nN5oUakTq43leZ5v2Xi/qoMalBkiO84xTND1qNAwbyTdkshBtM4bZTtFvp97Tdu4SHo6UVu4Fu5Jc3AA4aKo0QTuNhFWKI5oPDzDdHACzcCKAog+gh2zFjIc/CYT+iN52TIibIQgxW4zlk8NSheq7PNtxwbrku5p5Y2gu8PDTdQDLspWUh/4KHqwqfCgCPqYl6G/NXLl0z/8jSiK1Qhz+7UZwJkKnxBj/dcbafMpBE56PsQqvq+TwN+eL4oDrjUMHoKLpFcphJ+s5OXXmLBfyQJ5DIGHdqlkml6PpiORyoCjB4E/pBs8Xof8+EHfnuCKWVNkwF+DVNP8TobxQ3pF8qoANmm1P37o/AgnxOcRgbf5rsdQOVF6i6TVAcvAAOjx0kB8p/HfQiYqpjd2SJ8vCXgSwL8uvucP2BtNvQ6/DKXHSF4dUBGA36cHkz7DCWUtAI+5aBuVPg2s8h8OsEJ6ND8EUuooSq9BILcBqLj82iKFEdGTx3oqvAe/TKiAr0kZeLzSn0pjA6BzQjuApUQK/cN0YCBJtQEEkfYGcJY1ACkUlJ4NcDKK2JKeDeySNLDav2E6MHBJYBL7jxeD51cFJfeel2+pCgOT5Qp6vOQc6MWvEjJQUwj+9IrPcAVPDKacLLbOYv9FBkVkT76t5A70ShwucJgL/vF98CuW/IyLuMoC/DM52OnGGfBtkzIQ2cNXUew4sekF+MPVAbjfgrwA/Y5oR1yk3vDhPRLLyqkBph//LYIQS6PrKz/CdWczACuka6Ez7D/qBc908X5I4I5J5n/PxE1SnwmCNi79/kasuyRASukY7Y7X5bNsRE/fPDmrT1KsKZIKymGvC4AydYpyls+paeV78Itkts/bTBcsb5ASsF0KTDygXPbOzORaiqZ0PhcbGzax65HwPl6Z/T+xs/yHO+1hBCwJAOXLztFOtjfcK/hcd/yflINtSq7b9uI+2/TGmBlwVPIILbD6wgEvgheo1G2ifUTrQAAMBoWup2dVwYWGLRcpXj4WqQmrk50MLzBLBcaOJIPq7psGevi6q29v6xg/yhXnMdOEbWo7HN733Iu895DU8QMWTSZg+pppgp5V7XHRwVtfwOsTJI9bQscxx0TcYFg4pHeQwWUhLzhkGDgUuotlkZEBK2N1sTVJgZ/TEf4BeWZ/y7xynyKzAgYX7bBXJEW+THpmCOqU1RHX0Tqr8pcoLQMOdmAGchd6/vPdSXonPWDCPxmwQADibHC/YhiAUQAA0S0KWSVGA04AAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAD1ElEQVR4AeyXU5gcTRSG62L927b/aJLpnt241zGmd2Pbxl1s29Yotm3bdqY3nN5oUakTq43leZ5v2Xi/qoMalBkiO84xTND1qNAwbyTdkshBtM4bZTtFvp97Tdu4SHo6UVu4Fu5Jc3AA4aKo0QTuNhFWKI5oPDzDdHACzcCKAog+gh2zFjIc/CYT+iN52TIibIQgxW4zlk8NSheq7PNtxwbrku5p5Y2gu8PDTdQDLspWUh/4KHqwqfCgCPqYl6G/NXLl0z/8jSiK1Qhz+7UZwJkKnxBj/dcbafMpBE56PsQqvq+TwN+eL4oDrjUMHoKLpFcphJ+s5OXXmLBfyQJ5DIGHdqlkml6PpiORyoCjB4E/pBs8Xof8+EHfnuCKWVNkwF+DVNP8TobxQ3pF8qoANmm1P37o/AgnxOcRgbf5rsdQOVF6i6TVAcvAAOjx0kB8p/HfQiYqpjd2SJ8vCXgSwL8uvucP2BtNvQ6/DKXHSF4dUBGA36cHkz7DCWUtAI+5aBuVPg2s8h8OsEJ6ND8EUuooSq9BILcBqLj82iKFEdGTx3oqvAe/TKiAr0kZeLzSn0pjA6BzQjuApUQK/cN0YCBJtQEEkfYGcJY1ACkUlJ4NcDKK2JKeDeySNLDav2E6MHBJYBL7jxeD51cFJfeel2+pCgOT5Qp6vOQc6MWvEjJQUwj+9IrPcAVPDKacLLbOYv9FBkVkT76t5A70ShwucJgL/vF98CuW/IyLuMoC/DM52OnGGfBtkzIQ2cNXUew4sekF+MPVAbjfgrwA/Y5oR1yk3vDhPRLLyqkBph//LYIQS6PrKz/CdWczACuka6Ez7D/qBc908X5I4I5J5n/PxE1SnwmCNi79/kasuyRASukY7Y7X5bNsRE/fPDmrT1KsKZIKymGvC4AydYpyls+paeV78Itkts/bTBcsb5ASsF0KTDygXPbOzORaiqZ0PhcbGzax65HwPl6Z/T+xs/yHO+1hBCwJAOXLztFOtjfcK/hcd/yflINtSq7b9uI+2/TGmBlwVPIILbD6wgEvgheo1G2ifUTrQAAMBoWup2dVwYWGLRcpXj4WqQmrk50MLzBLBcaOJIPq7psGevi6q29v6xg/yhXnMdOEbWo7HN733Iu895DU8QMWTSZg+pppgp5V7XHRwVtfwOsTJI9bQscxx0TcYFg4pHeQwWUhLzhkGDgUuotlkZEBK2N1sTVJgZ/TEf4BeWZ/y7xynyKzAgYX7bBXJEW+THpmCOqU1RHX0Tqr8pcoLQMOdmAGchd6/vPdSXonPWDCPxmwQADibHC/YhiAUQAA0S0KWSVGA04AAAAASUVORK5CYII=\"\n  },\n  \"361a3082-0278-4583-a16f-72a527f973e4\": {\n    \"name\": \"eWBM eFA500 FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"2ffd6452-01da-471f-821b-ea4bf6c8676a\": {\n    \"name\": \"IDPrime 941 Fido\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"30b5035e-d297-4ff7-b00b-addc96ba6a98\": {\n    \"name\": \"OneSpan DIGIPASS FX7\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"692db549-7ae5-44d5-a1e5-dd20a493b723\": {\n    \"name\": \"HID Crescendo Key\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"bbf4b6a7-679d-f6fc-c4f2-8ac0ddf9015a\": {\n    \"name\": \"Excelsecu eSecu FIDO2 PRO Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d\": {\n    \"name\": \"Feitian iePass FIDO Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"23786452-f02d-4344-87ed-aaf703726881\": {\n    \"name\": \"SafeNet eToken Fusion CC\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"234cd403-35a2-4cc2-8015-77ea280c77f5\": {\n    \"name\": \"Feitian ePass FIDO2-NFC Series (CTAP2.1, CTAP2.0, U2F)\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"aeb6569c-f8fb-4950-ac60-24ca2bbe2e52\": {\n    \"name\": \"HID Crescendo C2300\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"87dbc5a1-4c94-4dc8-8a47-97d800fd1f3c\": {\n    \"name\": \"eWBM eFA320 FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"58276709-bb4b-4bb3-baf1-60eea99282a7\": {\n    \"name\": \"YubiKey Bio Series - Multi-protocol Edition 1VDJSN\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"7d2afadd-bf6b-44a2-a66b-e831fceb8eff\": {\n    \"name\": \"Taglio CTAP2.1 EP\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"20ac7a17-c814-4833-93fe-539f0d5e3389\": {\n    \"name\": \"YubiKey 5 Series (Enterprise Profile)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"9f0d8150-baa5-4c00-9299-ad62c8bb4e87\": {\n    \"name\": \"GoTrust Idem Card FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAjCAYAAAD17ghaAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACHDwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKL2lDQ1BJQ0MgUHJvZmlsZQAASMedlndUVNcWh8+9d3qhzTDSGXqTLjCA9C4gHQRRGGYGGMoAwwxNbIioQEQREQFFkKCAAaOhSKyIYiEoqGAPSBBQYjCKqKhkRtZKfHl57+Xl98e939pn73P32XuftS4AJE8fLi8FlgIgmSfgB3o401eFR9Cx/QAGeIABpgAwWempvkHuwUAkLzcXerrICfyL3gwBSPy+ZejpT6eD/0/SrFS+AADIX8TmbE46S8T5Ik7KFKSK7TMipsYkihlGiZkvSlDEcmKOW+Sln30W2VHM7GQeW8TinFPZyWwx94h4e4aQI2LER8QFGVxOpohvi1gzSZjMFfFbcWwyh5kOAIoktgs4rHgRm4iYxA8OdBHxcgBwpLgvOOYLFnCyBOJDuaSkZvO5cfECui5Lj25qbc2ge3IykzgCgaE/k5XI5LPpLinJqUxeNgCLZ/4sGXFt6aIiW5paW1oamhmZflGo/7r4NyXu7SK9CvjcM4jW94ftr/xS6gBgzIpqs+sPW8x+ADq2AiB3/w+b5iEAJEV9a7/xxXlo4nmJFwhSbYyNMzMzjbgclpG4oL/rfzr8DX3xPSPxdr+Xh+7KiWUKkwR0cd1YKUkpQj49PZXJ4tAN/zzE/zjwr/NYGsiJ5fA5PFFEqGjKuLw4Ubt5bK6Am8Kjc3n/qYn/MOxPWpxrkSj1nwA1yghI3aAC5Oc+gKIQARJ5UNz13/vmgw8F4psXpjqxOPefBf37rnCJ+JHOjfsc5xIYTGcJ+RmLa+JrCdCAACQBFcgDFaABdIEhMANWwBY4AjewAviBYBAO1gIWiAfJgA8yQS7YDApAEdgF9oJKUAPqQSNoASdABzgNLoDL4Dq4Ce6AB2AEjIPnYAa8AfMQBGEhMkSB5CFVSAsygMwgBmQPuUE+UCAUDkVDcRAPEkK50BaoCCqFKqFaqBH6FjoFXYCuQgPQPWgUmoJ+hd7DCEyCqbAyrA0bwwzYCfaGg+E1cBycBufA+fBOuAKug4/B7fAF+Dp8Bx6Bn8OzCECICA1RQwwRBuKC+CERSCzCRzYghUg5Uoe0IF1IL3ILGUGmkXcoDIqCoqMMUbYoT1QIioVKQ21AFaMqUUdR7age1C3UKGoG9QlNRiuhDdA2aC/0KnQcOhNdgC5HN6Db0JfQd9Dj6DcYDIaG0cFYYTwx4ZgEzDpMMeYAphVzHjOAGcPMYrFYeawB1g7rh2ViBdgC7H7sMew57CB2HPsWR8Sp4sxw7rgIHA+XhyvHNeHO4gZxE7h5vBReC2+D98Oz8dn4Enw9vgt/Az+OnydIE3QIdoRgQgJhM6GC0EK4RHhIeEUkEtWJ1sQAIpe4iVhBPE68QhwlviPJkPRJLqRIkpC0k3SEdJ50j/SKTCZrkx3JEWQBeSe5kXyR/Jj8VoIiYSThJcGW2ChRJdEuMSjxQhIvqSXpJLlWMkeyXPKk5A3JaSm8lLaUixRTaoNUldQpqWGpWWmKtKm0n3SydLF0k/RV6UkZrIy2jJsMWyZf5rDMRZkxCkLRoLhQWJQtlHrKJco4FUPVoXpRE6hF1G+o/dQZWRnZZbKhslmyVbJnZEdoCE2b5kVLopXQTtCGaO+XKC9xWsJZsmNJy5LBJXNyinKOchy5QrlWuTty7+Xp8m7yifK75TvkHymgFPQVAhQyFQ4qXFKYVqQq2iqyFAsVTyjeV4KV9JUCldYpHVbqU5pVVlH2UE5V3q98UXlahabiqJKgUqZyVmVKlaJqr8pVLVM9p/qMLkt3oifRK+g99Bk1JTVPNaFarVq/2ry6jnqIep56q/ojDYIGQyNWo0yjW2NGU1XTVzNXs1nzvhZei6EVr7VPq1drTltHO0x7m3aH9qSOnI6XTo5Os85DXbKug26abp3ubT2MHkMvUe+A3k19WN9CP16/Sv+GAWxgacA1OGAwsBS91Hopb2nd0mFDkqGTYYZhs+GoEc3IxyjPqMPohbGmcYTxbuNe408mFiZJJvUmD0xlTFeY5pl2mf5qpm/GMqsyu21ONnc332jeaf5ymcEyzrKDy+5aUCx8LbZZdFt8tLSy5Fu2WE5ZaVpFW1VbDTOoDH9GMeOKNdra2Xqj9WnrdzaWNgKbEza/2BraJto22U4u11nOWV6/fMxO3Y5pV2s3Yk+3j7Y/ZD/ioObAdKhzeOKo4ch2bHCccNJzSnA65vTC2cSZ79zmPOdi47Le5bwr4urhWuja7ybjFuJW6fbYXd09zr3ZfcbDwmOdx3lPtKe3527PYS9lL5ZXo9fMCqsV61f0eJO8g7wrvZ/46Pvwfbp8Yd8Vvnt8H67UWslb2eEH/Lz89vg98tfxT/P/PgAT4B9QFfA00DQwN7A3iBIUFdQU9CbYObgk+EGIbogwpDtUMjQytDF0Lsw1rDRsZJXxqvWrrocrhHPDOyOwEaERDRGzq91W7109HmkRWRA5tEZnTdaaq2sV1iatPRMlGcWMOhmNjg6Lbor+wPRj1jFnY7xiqmNmWC6sfaznbEd2GXuKY8cp5UzE2sWWxk7G2cXtiZuKd4gvj5/munAruS8TPBNqEuYS/RKPJC4khSW1JuOSo5NP8WR4ibyeFJWUrJSBVIPUgtSRNJu0vWkzfG9+QzqUvia9U0AV/Uz1CXWFW4WjGfYZVRlvM0MzT2ZJZ/Gy+rL1s3dkT+S453y9DrWOta47Vy13c+7oeqf1tRugDTEbujdqbMzfOL7JY9PRzYTNiZt/yDPJK817vSVsS1e+cv6m/LGtHlubCyQK+AXD22y31WxHbedu799hvmP/jk+F7MJrRSZF5UUfilnF174y/ariq4WdsTv7SyxLDu7C7OLtGtrtsPtoqXRpTunYHt897WX0ssKy13uj9l4tX1Zes4+wT7hvpMKnonO/5v5d+z9UxlfeqXKuaq1Wqt5RPXeAfWDwoOPBlhrlmqKa94e4h+7WetS212nXlR/GHM44/LQ+tL73a8bXjQ0KDUUNH4/wjowcDTza02jV2Nik1FTSDDcLm6eORR67+Y3rN50thi21rbTWouPguPD4s2+jvx064X2i+yTjZMt3Wt9Vt1HaCtuh9uz2mY74jpHO8M6BUytOdXfZdrV9b/T9kdNqp6vOyJ4pOUs4m3924VzOudnzqeenL8RdGOuO6n5wcdXF2z0BPf2XvC9duex++WKvU++5K3ZXTl+1uXrqGuNax3XL6+19Fn1tP1j80NZv2d9+w+pG503rm10DywfODjoMXrjleuvyba/b1++svDMwFDJ0dzhyeOQu++7kvaR7L+9n3J9/sOkh+mHhI6lH5Y+VHtf9qPdj64jlyJlR19G+J0FPHoyxxp7/lP7Th/H8p+Sn5ROqE42TZpOnp9ynbj5b/Wz8eerz+emCn6V/rn6h++K7Xxx/6ZtZNTP+kv9y4dfiV/Kvjrxe9rp71n/28ZvkN/NzhW/l3x59x3jX+z7s/cR85gfsh4qPeh+7Pnl/eriQvLDwG/eE8/s3BCkeAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIXRFWHRDcmVhdGlvbiBUaW1lADIwMTg6MDU6MjggMTY6NDI6MTT9hwrfAAAIHUlEQVRYR51XC1BU5xX+dllgQd4PURAfiShaNG1i7Bhtm05KUknTWB+NQa0YG2ODljoOGk1iO51qNGQck9okRJs04Iw6puN0TExTaOsYS7SSphpf1KAVBRZhWR4rILt7b7/z37vsQhaC/S7/svz3vM/5z/mx6ASGCZ2P/Fgs8pf66INfjMV4OWxYzd/Dg+ZXYEHlJ5/jvgWb8OjqHWhscan9O1UuGF4EhMQU3trhRt7ql3GqshpIiAF8PqDrNpYV5OH1F1cgJjoqKFLCI+IHN2x4ETCV/3zbH5A8cRFOVV8CRicDUZFANJfVivIDFaj69xeKTikkj6bRFH1w5YJBItDf6j9Vnsa8Z3bQWy8QS6+t5jt3t4rA1s0F2LzqcWOP6L1ap4yKGDfG3CEGC4QYEAyNjx+115v0KY+u15GWpyMnX8c0WUt1ZD+hI+lhfWHRTt3r9ZnUBhpXbdTPIVw/jxG6Y80Wc5dyfQG5wRi0BvKLd2N/2QfMcyxgZ5gFku+WdoycOAZV+3+NuzPTjH3CtfsdONYW01EfwpDAHY1PB/+2IWNfKeKXzDcIB8CiMVHB1fv2H49hZWEJMMIOxIzgDu3TWP4dXTTEhvJXirD0sTkGMdFTfQZ1314AX3cjFbMu+ClQhahi7uXTgsjkiRhz7BDsOdnqDVgfFqayLwJfXG/C7CW/ws3LzF9KolGe8qanVylfu3YhXnu+QEgVvM2taJj3FDqrjtLHVO7Y1L5EwId2qrZQRLz6NPY93G9GbO4iZB4tJ3mYMq/PAMu4H9HDCK5wQ7GPXje1YsaD96LinReYiWghU3Csfg7O0tfoawyFRCtBugq5C2HWRGRWHYbu9TEy86Fr7aRL4nsxiWJpnC0pA1nOc0qWMq++ycWz3ANEmsp7bsMWbsXHH+3C6fe29Slve/cQLlji4Cp9i/6mkFmUi89urjaM3Lodk3x1iPrmfYiePRPZvhsYub2EKWgmt4eUOnli4Wmtg+ZmSgkVAYezDaNzlgJpSTxDXqSPTkL9X3crAkH3yc9w44cr4GmuUeEWMYY33arQEn9cgPSDbxjERAeFh9msLCPWkYnajBnwNTSRL4wGtWNyVyOsUXYzQSJOMqGWxv7CVJi4NmsersyaBa35JpVL1QuLF71ogH3a1zCprraf8pK3jyB+aj5i6NDrbE5+2Mam01ivioJRnLLMFCioPWPTLAsF90kpslH8JkdRwu1UQib8pQITzv4N4Znpiu5E9UVE5ORjw5a9QBxTFhGOwk0Bw+QIG9L7I2CA6AxS7EcY7GSUEpIi60bq9h3I1usxIvc76v31my5Mm7cB33qkCB5hT44jE48ij5hNDPkKBAwYBMoutXgq6FXKxmfVvqB9cSHG3rMM5y5eAzKYnrBQPgbwZfcGScFAyAFSj8Ugb311Dy5aYuA+eAjW9BTj9IiBbp6kLs4HvyZpYEEYOgXsTAMZBMIk3iuZ1khcuesBNP5iHVOTyHnDwSRGd7NZOVwoLlyAjT9bQCN4xCgqMtxoTn5I7RhFGEDAAE4vtQZATLLKY2Hn6vbAw0knPUB2da0XWkML7v16Ftpq38PL6/PZiGiQMPGXPVwiE4CSwycYQREgV4giNDocP3k8jW4mvV5Tp8Edl4DKD3bi00NbEW82K1cnvTfHdbA0+S6S5AlG/wiEqAGbmmyGajkNGjpV10v77W5Maj+Hh76RpejaeTeYtfgFvPH7I7ykRCmeYIjkr45AiBqQrqWhh+J62EwbkLByJabqHUhaExhMT/9yDxLGPY6T/6phD+AEFW2sqc5bRrsVDB0BCX1QDdg4qfzIdrG3T78HEVOmYHJzE0bt5ag28dbBSlgmzMfesg+BdE5EuTdIFCUNnCclxctMSm5TthHF/lFWGlXqmWP1hU3k8jUH/nzijLxCWEIixp9h17vwd9hSOCuI059fQcoDq/DMul28MzDcfq9v8zTcaMaSRd+FfvUwipbnKXqBt1EGEgt3QGqUAZGR9FjGr4AFpDMVcxc+hyk/KEadw2nsE228F8xc/CJmPlQIZ1uHeW+gCC95G1uRM3k86i/tx74da0wO8rxZzgkaD2/dNdoYriKgM7HQeLsi+m5EuSt+w4r+B5BqCpVKFo+a2/DTZ+cjlS32pa3vAolBVzSpmXY353scjv5uA3LnTDf2ia4Tp1D/yFJ4uhpYyMlUakxQL0e3LT4Fk9p4syZMA9RXlB05geUbOIaloyWaTUZwi91NGlWMjFdzT/JMbNu8HJueDtyIvc1O3Ji7DLc+reCBTSO1TXGI1x7cROyM7yHz48Ow0AnZVwYIY/C9sLhkH155qYyDhUcwiqNZveOSOun1sOs58cRTj+HAziKDwUTjT9bBVV5KxXGktlOp8PmouhUR9jRkVB7gReV+g1jqTeTKhSQUvJpPn/3kFl7J5xrX8KlPqu9Z31+nO1raTCoDzlf38Cpu51U8Ua9BJtdY/RLXBf59HrG6s7TMpJRrf/9r/JcMkIjwpw/V52v11DmrdQv/L3j/+GfmroHOiuP6f2KzqCRaKazBeK5x+kWkcS9KbyhYb1IKRK6xgjHo/wVDwcOrVb3k+exxhjuFgZahI2Ikz02IuT8XY97fB9tIKT6VvEFhdJ4hISICNjatfR41GaPQffYs1Y7uU64xz9YIO+6q+gTj//mhoVx8C7CGhkTgTnD78n/1q9MfZs4jGepUhjqeuU7Snbv2mhR3hjsyQGNh+jPo/uiYXpeXrzuKtgT9Nxn6/7+h8H/VQCiIkKFyHRrA/wC4e+O+Z1cn4QAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"12ded745-4bed-47d4-abaa-e713f51d6393\": {\n    \"name\": \"Feitian AllinOne FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"88bbd2f0-342a-42e7-9729-dd158be5407a\": {\n    \"name\": \"Precision InnaIT Key FIDO 2 Level 2 certified\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"dd86a2da-86a0-4cbe-b462-4bd31f57bc6f\": {\n    \"name\": \"YubiKey Bio FIDO Edition\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"34f5766d-1536-4a24-9033-0e294e510fb0\": {\n    \"name\": \"YubiKey 5 Series with NFC Preview\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"83c47309-aabb-4108-8470-8be838b573cb\": {\n    \"name\": \"YubiKey Bio Series (Enterprise Profile)\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\"\n  },\n  \"be727034-574a-f799-5c76-0929e0430973\": {\n    \"name\": \"Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"092277e5-8437-46b5-b911-ea64b294acb7\": {\n    \"name\": \"Taglio CTAP2.1 CS\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"ca87cb70-4c1b-4579-a8e8-4efdd7c007e0\": {\n    \"name\": \"FIDO Alliance TruU Sample FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPMAAADCCAMAAACrDgQFAAACdlBMVEUAAAD/AAD/gAD/VVX/gED/ZjP/gCv/bUn/gED/cTn/gDP/dC7/gED/djv/bTf/dzP/cED/eDz/cTnyeTbyc0DzeT3zdDr0ejf0dTX1ej31djv2cTn2djf2cj73dzz3czr3eDj4eDz4dTr4eDn4dTf4eTz4djv5eTn5djj5cz35dzv5dzn5dDf6dzz6dTr6eDn6dTj6eDr6djn2dDz2djv2dDr2dzn3dzz3dTv3dzr3dTn3eDz3djv3eDr3djn4dDj4dTr4dzn4dTn4dzv4dzr4dTn4dzz4djv5djn5djv5dTr5dTn5dTv5dzr5dzn3djv3djv3dTr3djr3dTn3djv3dTv3dzr4djr4dzn4djv4djr4dzn4djv4dTr4dTn4djn4djr4dzn4djv5djr5dzn5dTr5djn5djr3djr3dzn3djv3djr3dzr3dTv4djr4dTr4djn4dTv4djr4djr4dzr4djr4dzr4dTv4djr4djr4djr4djn5dzr5djr5dzr5dTv5djr3djr3dTv3djr3djr3djn4dzv4djr4dzr4dzv4djr4dTr4djr4djr4djr4djv4dzr4dzv4djr4dTr4djr4dTn4djr4djr4djr4djv5djr5djr5djr5dzn3djr3dTr3djr4djr4djr4djr4djv4djr4dzn4djr4dTr4djr4djr4djr4djr4djr4djr4djn4djr4djr4djr4djr4djr4dzr4djr4djr4djv4djr4djr4djr4djr5djr3djr4djr4djr4djr4dzr4djr4djr4djn4djr4djr4djr4djr4djr4djr4djr4djr4djr4djr4djr///8UBbnaAAAA0HRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJicoKSorLS4vMDEyNTY3ODk6PD0+P0BBQkNERkdISUtMTU5QUlNVV1haW19gYWJjZGVmZ2hqa2xtb3BzdHV3eHx9f4CBgoSFh4iJiouMjZCRkpSVmZqcnZ6foaKkpaanqaqrrK6vsLO0tbe5u7y9vr/AwcLExcbHyMnKy87P0NHS1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+CPW/JAAAAAFi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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPMAAADCCAMAAACrDgQFAAACdlBMVEUAAAD/AAD/gAD/VVX/gED/ZjP/gCv/bUn/gED/cTn/gDP/dC7/gED/djv/bTf/dzP/cED/eDz/cTnyeTbyc0DzeT3zdDr0ejf0dTX1ej31djv2cTn2djf2cj73dzz3czr3eDj4eDz4dTr4eDn4dTf4eTz4djv5eTn5djj5cz35dzv5dzn5dDf6dzz6dTr6eDn6dTj6eDr6djn2dDz2djv2dDr2dzn3dzz3dTv3dzr3dTn3eDz3djv3eDr3djn4dDj4dTr4dzn4dTn4dzv4dzr4dTn4dzz4djv5djn5djv5dTr5dTn5dTv5dzr5dzn3djv3djv3dTr3djr3dTn3djv3dTv3dzr4djr4dzn4djv4djr4dzn4djv4dTr4dTn4djn4djr4dzn4djv5djr5dzn5dTr5djn5djr3djr3dzn3djv3djr3dzr3dTv4djr4dTr4djn4dTv4djr4djr4dzr4djr4dzr4dTv4djr4djr4djr4djn5dzr5djr5dzr5dTv5djr3djr3dTv3djr3djr3djn4dzv4djr4dzr4dzv4djr4dTr4djr4djr4djr4djv4dzr4dzv4djr4dTr4djr4dTn4djr4djr4djr4djv5djr5djr5djr5dzn3djr3dTr3djr4djr4djr4djr4djv4djr4dzn4djr4dTr4djr4djr4djr4djr4djr4djr4djn4djr4djr4djr4djr4djr4dzr4djr4djr4djv4djr4djr4djr4djr5djr3djr4djr4djr4djr4dzr4djr4djr4djn4djr4djr4djr4djr4djr4djr4djr4djr4djr4djr4djr///8UBbnaAAAA0HRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJicoKSorLS4vMDEyNTY3ODk6PD0+P0BBQkNERkdISUtMTU5QUlNVV1haW19gYWJjZGVmZ2hqa2xtb3BzdHV3eHx9f4CBgoSFh4iJiouMjZCRkpSVmZqcnZ6foaKkpaanqaqrrK6vsLO0tbe5u7y9vr/AwcLExcbHyMnKy87P0NHS1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+CPW/JAAAAAFiS0dE0XnR/woAAAdtSURBVHja3dz5WxVlFMDxc1FcwMQCFStKW9UyLbVULEoNMto3CmilfUFEW2jfvNCe5VIB3rTSLKXNMgpxpQQvXv+kwsd6r3rvzLzve855z3vPb/Jc7swXcD4zc2cGINNMix/JhVk9E3RmzjrvizvKQXcqNnldvKU6BvqTV73d2+JfaoaB2eTXdHtZvKthFJhPYcM+74r7GovAbkoaD3pVfKilFOznrJZBb4oPx88BnPGGa02Qc4DrznLAHfFcm4EcxnVXToIcPCPEcm0Hso9c24PsG9c4IPvEdSp+LnCMIK5RQQ6euTK47lwInFPxVU6CLJtrKpDlck0JcvCMccQ1NcjyuOYAWRbXXCAL4poR5BCu1+cmyAK45gfZNdduQHbJtTuQXXHtFuQwrvtJQJ4Ekgefaxkgs3ItBuTgmd+OVtw+n2IFHygneFMkrr+lAHlOLZTuofjrweB6R81wiv95+84AqEP8YAuR614KkIe2sLVDv5J2osMzG677Gsfhr1Dx0IFv4uje3EWHqNQ35ZoE5MKGvf++dfLYf+Qmur07E65JQM6v+f3omy879u+CLsK9+Om6XK+eRbFJPXY50M9j/vvSItKjNa2ja5IjZPU5cqX6ovpddDjlmgZk9QHEqrQvl+5J+9u6xBXXRCCrBQzRrKaO5DoULa57G0bjL7bsuG1o7fG/iHbys6nBXNOBrCZxwib6KNLEB+nZuSYEWU3ypH3sJo6TMZm5JgVZzbKTXlPQxXLSLQPXpCCrUTSrWXTyL2BzNf3J8A20IKupzPTCTDtMHQtouaYBeW2GklUZX5qONA/XJCBPjacyZBxPc2akGbhmADkbzWk//s7MkvSvHI+/ckW3EWg4fmUWDTvzsn3LCUj7cU49O8hBNKtZ7t1nJ0Egq2kK+L6CH/z6jCwQ5GCa1SwOOgog4ZoO5BCa1bQGHvuQcE0Espp4yLdnRpqWaxKQw2lWUx9ymEvCNQHIaurCNwahHza5v1Yp6xFypklE2PRmRVoe19lBjkZzFKRlcR0EcjSaoyGdZp5jriPewRpMc0SkhXBdsTHaSlZGfcPWiKejnXE9e23ENYxHfsswpB1zHQayDs3RkU7neoo4kHVo1kFazUDLRFkg69GshXQ612MFgaxLsx7SanpYuI4Gsi7Nukhzcq39SImoNGsjzcZ1VJANaNZHmoXryCCb0GyCNDnX0UE2o9kEaVquy4wum60z22gYXbaJzXWx2d1MCcNNqhbSag4gcq0HsjnNpkjjc60LsjnN5kjjch2r3ma8+DHmi118xHisudYH2YpmNW0215vbcD17jcWSW61+2JP2WCz6yOoZjCDb0qzmXquLsM24LrO8j6Xeds/e8t4Kfa6LbW8vTlhvPi8+ZLcGmlybgoxBs5pm6xsruqOvxUz7+w+bEXYMzJH+f5ZGXthS62XZ0IyCtIPmKpyd3jaPmltxki2RZm22pRkLac7meqxka6TZmhOIJyItkeZqTqLeBdnsRfNy1DM0dkgzNePQjIQ0U3MVIE+b+OZW7GQrpFma8WhGQZqluR4/2QZpjuYEyWeE5kgzNCeJHm/RLLh5OU2yOdL0zdg02yNN31wFZNMmtLmVLtkUaepmCpptkaZurqdMNkSauDlBfPmOEdK0zYPkT55aIa65mTrZCGnSZjqarZAmba4ChmkT1dzGkWyANGEzLc1q6gQ11/IkQ94XYpo3sN0Io3vhGFlzkvHWiEYhzc/wJcPo7SKafyxkbIarRTQvAdZ5U0DzG7zJUNLjvLl3AnMz3OG8+XbuZIh96rh5vYMHzZ9/0GnzwFRwMI87bX7MRTKM2OKw+TtH95zPTzlrTl0JjuZlZ80vuUqG0/5w1NxT4qwZbnbUfBM4nA+cNH/sMhkm9zlo/tvxk3AedtDc4DYZhm9ib/4m33EzXDbI3Hz4cnA+LzA3P+8+Gcb+ytq8c5yAZrietXkpiJj3GJs/lJEMZfvZmvsmC2mG+9ma75OSDHkdTM1fCnrEaMgVF1jNg7NA0KxgaW6WlBxyxQVSM8dVFDqzmKG5EoRNnLx5lbRkKN1N3Mx1FYXO3EPcfLe8ZMj7nLQ5+9PhXc70AcLm5AwQOU8TNj8lMxlGbiVr3jZaaDOUp6iaK0DsvE7U/JrcZCj+k6R51wTBzXArSfMtkpMh9glB87qY6GY47yB6c/+FIHweRW9+RHoyjNiM3Lx1pPhmuCKF2pxaCB7Mi6jNLT4kQ9FviM3dp3rRDDciNt8Ansz7aM0f+ZIMZx9Aav5rijfN8BBS84P+JMPwjSjNX+d71AyXDiI0H54LXs1zCM3P+pUMp+ywbt5Z5FkzLLFuvg68m3ctm9/xLxkm7bVq3n+mh81pz7gwaa73MTntGRcGzYlhXjar22f1m5MzwdNpMm5e5msyFHQZNv9U6G0zXGPYfC14PG8bNb/lczJM3G3QvPd0r5vhLoPmO/1Ohtga7ebPYp43wwX9ms0D08D7eVKz+Qn/k2HkFq3m70flQDMsSGk0p66CnJhXNZpfyY1kKJ4X+aXzihnW5x9V2BF64n5f3AAAAABJRU5ErkJggg==\"\n  },\n  \"58b44d0b-0a7c-f33a-fd48-f7153c871352\": {\n    \"name\": \"Ledger Nano S Plus FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"454e5346-4944-4ffd-6c93-8e9267193e9b\": {\n    \"name\": \"Ensurity AUTH BioPro\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKgAAACoCAYAAAB0S6W0AAAACXBIWXMAAC4jAAAuIwF4pT92AAAFxGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OTc3NywgMjAyMy8wNi8yNS0yMzo1NzoxNCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDI0LTAzLTA1VDE3OjA0OjIwKzA1OjMwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyNC0wMy0wNVQxNzowNzo1MSswNTozMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wMy0wNVQxNzowNzo1MSswNTozMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZmY0OGY4ZGUtZDYxOC00MjhkLTgwOGYtMzE3MDY4OTM3NzFkIiB4bXBNTTpEb2N1bWVu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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKgAAACoCAYAAAB0S6W0AAAACXBIWXMAAC4jAAAuIwF4pT92AAAFxGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OTc3NywgMjAyMy8wNi8yNS0yMzo1NzoxNCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDI0LTAzLTA1VDE3OjA0OjIwKzA1OjMwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyNC0wMy0wNVQxNzowNzo1MSswNTozMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wMy0wNVQxNzowNzo1MSswNTozMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZmY0OGY4ZGUtZDYxOC00MjhkLTgwOGYtMzE3MDY4OTM3NzFkIiB4bXBNTTpEb2N1bWVu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\"\n  },\n  \"e77e3c64-05e3-428b-8824-0cbeb04b829d\": {\n    \"name\": \"Security Key NFC by Yubico\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"7d1351a6-e097-4852-b8bf-c9ac5c9ce4a3\": {\n    \"name\": \"YubiKey Bio Series - Multi-protocol Edition\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"07a9f89c-6407-4594-9d56-621d5f1e358b\": {\n    \"name\": \"NXP Semiconductros FIDO2 Conformance Testing CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"d61d3b87-3e7c-4aea-9c50-441c371903ad\": {\n    \"name\": \"KeyVault Secp256R1 FIDO2 CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"5ca1ab1e-1337-fa57-f1d0-a117e71ca702\": {\n    \"name\": \"Allthenticator App: roaming BLE FIDO2 Allthenticator for Windows, Mac, Linux, and Allthenticate door readers\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"b92c3f9a-c014-4056-887f-140a2501163b\": {\n    \"name\": \"Security Key by Yubico\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"54d9fee8-e621-4291-8b18-7157b99c5bec\": {\n    \"name\": \"HID Crescendo Enabled\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"a25342c0-3cdc-4414-8e46-f4807fca511c\": {\n    \"name\": \"YubiKey 5 Series with NFC\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"20f0be98-9af9-986a-4b42-8eca4acb28e4\": {\n    \"name\": \"Excelsecu eSecu FIDO2 Fingerprint Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"ca4cff1b-5a81-4404-8194-59aabcf1660b\": {\n    \"name\": \"IDPrime 3930 FIDO\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"ab32f0c6-2239-afbb-c470-d2ef4e254db6\": {\n    \"name\": \"TEST (DUMMY RECORD)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"760eda36-00aa-4d29-855b-4012a182cdeb\": {\n    \"name\": \"Security Key NFC by Yubico Preview\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\"\n  },\n  \"6028b017-b1d4-4c02-b4b3-afcdafc96bb2\": {\n    \"name\": \"Windows Hello\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMDc4ZDQ7c3Ryb2tlLXdpZHRoOjBweDt9PC9zdHlsZT48L2RlZnM+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIyNC4yNSIgeT0iMjQuMjUiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTMzLjQiIHk9IjI0LjI1IiB3aWR0aD0iOTguMzUiIGhlaWdodD0iOTguMzUiLz48cmVjdCBjbGFzcz0iY2xzLTEiIHg9IjI0LjI1IiB5PSIxMzMuNCIgd2lkdGg9Ijk4LjM1IiBoZWlnaHQ9Ijk4LjM1Ii8+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxMzMuNCIgeT0iMTMzLjQiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjwvc3ZnPg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMDc4ZDQ7c3Ryb2tlLXdpZHRoOjBweDt9PC9zdHlsZT48L2RlZnM+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIyNC4yNSIgeT0iMjQuMjUiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTMzLjQiIHk9IjI0LjI1IiB3aWR0aD0iOTguMzUiIGhlaWdodD0iOTguMzUiLz48cmVjdCBjbGFzcz0iY2xzLTEiIHg9IjI0LjI1IiB5PSIxMzMuNCIgd2lkdGg9Ijk4LjM1IiBoZWlnaHQ9Ijk4LjM1Ii8+PHJlY3QgY2xhc3M9ImNscy0xIiB4PSIxMzMuNCIgeT0iMTMzLjQiIHdpZHRoPSI5OC4zNSIgaGVpZ2h0PSI5OC4zNSIvPjwvc3ZnPg==\"\n  },\n  \"30b5035e-d297-4fc1-b00b-addc96ba6a97\": {\n    \"name\": \"OneSpan FIDO Touch\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"1ac71f64-468d-4fe0-bef1-0e5f2f551f18\": {\n    \"name\": \"YubiKey 5 Series with NFC (Enterprise Profile)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"6d44ba9b-f6ec-2e49-b930-0c8fe920cb73\": {\n    \"name\": \"Security Key by Yubico with NFC\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"5ca1ab1e-fa57-1337-f1d0-a117371ca702\": {\n    \"name\": \"Allthenticator Android App: roaming BLE FIDO2 Allthenticator for Windows, Mac, Linux, and Allthenticate door readers\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"eabb46cc-e241-80bf-ae9e-96fa6d2975cf\": {\n    \"name\": \"TOKEN2 PIN Plus Security Key Series \",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"53414d53-554e-4700-0000-000000000000\": {\n    \"name\": \"Samsung Pass\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgd2lkdGg9IjUycHgiIGhlaWdodD0iNTJweCIgdmlld0JveD0iMCAwIDUyLjAgNTIuMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGRlZnM+PGNsaXBQYXRoIGlkPSJpMCI+PHBhdGggZD0iTTM2MCwwIEwzNjAsODAwIEwwLDgwMCBMMCwwIEwzNjAsMCBaIj48L3BhdGg+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImkxIj48cGF0aCBkPSJNMjYsMCBDMzMuOTkxMDI3OCwwIDQxLjEzOTU4MzMsMC45NzUgNDUuOTA4Nzc3OCw1Ljc3Nzc3Nzc4IEM0OS43MTAxOTQ0LDkuNjA1OTE2NjcgNTIsMTUuODY1MDU1NiA1MiwyNiBDNTIsMzYuMTM0OTQ0NCA0OS43MDk4MzMzLDQyLjM5NDQ0NDQgNDUuOTA4MDU1Niw0Ni4yMjI1ODMzIEM0MS4xMzg4NjExLDUxLjAyNDYzODkgMzMuOTkwMzA1Niw1MiAyNiw1MiBDMTguMDA4OTcyMiw1MiAxMC44NjA3Nzc4LDUxLjAyNDYzODkgNi4wOTE1ODMzMyw0Ni4yMjI1ODMzIEMyLjI5MDE2NjY3LDQyLjM5NDQ0NDQgMCwzNi4xMzQ5NDQ0IDAsMjYgQzAsMTUuODY1MDU1NiAyLjI4OTgwNTU2LDkuNjA1NTU1NTYgNi4wOTA4NjExMSw1Ljc3Nzc3Nzc4IEMxMC44NjAwNTU2LDAuOTc1IDE4LjAwODYxMTEsMCAyNiwwIFoiPjwvcGF0aD48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iaTIiIHgxPSIyNnB4IiB5MT0iNTJweCIgeDI9IjI2cHgiIHkyPSIwLjE5NTkyMTE0OHB4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iIzI5MjlCMiIgb2Zmc2V0PSIwJSI+PC9zdG9wPjxzdG9wIHN0b3AtY29sb3I9IiMxQTQwQ0MiIG9mZnNldD0iMTAwJSI+PC9zdG9wPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJpMyI+PHBhdGggZD0iTTM3LjE5NDQ0NDQsMCBMMzcuMTk0NDQ0NCw1LjcyMjIyMjIyIEwwLDUuNzIyMjIyMjIgTDAsMCBMMzcuMTk0NDQ0NCwwIFoiPjwvcGF0aD48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iaTQiPjxwYXRoIGQ9Ik0xLjg4MzQyMjEzLDAgQzIuNjIwNzU5ODcsMCAzLjY0MzU2NDAyLDAuMTgxNjEwODcxIDMuNjQzNTY0MDIsMS4zMjExMTgxOSBMMy42NDM1NjQwMiwxLjY4OTExNTYyIEwyLjM0Mjc5MzM5LDEuNjg5MTE1NjIgTDIuMzQyNzkzMzksMS4zNjQ5MDY1OSBDMi4zNDI3OTMzOSwxLjA3OTU3NTczIDIuMTYzMTk2NjQsMC44ODkxNTMzNzEgMS44NTgxNzY4LDAuODg5MTUzMzcxIEMxLjUyOTMxNzg4LDAuODg5MTUzMzcxIDEuNDE2NDgzOTgsMS4wNzUyNzA4OCAxLjM4MDg1OTI3LDEuMjQyMTUxMDkgQzEuMzY2Nzk2ODgsMS4zMDAyNjY1NyAxLjM2MDkwNDA4LDEuNDExNzIxODQgMS4zODYxNDk0MSwxLjUxNzI1NzkzIEMxLjUzNDYwODAyLDIuMTMwNDk3MyAzLjUxMjQ0OTAyLDIuNDU2MTE4ODcgMy43MzI1NTg4MywzLjU1NTUzNzI3IEMzLjc1MzcxOTM3LDMuNjY3MDU5OCAzLjgwMDc5NDg4LDMuOTYxMTM0ODggMy43MzgwNDk4Niw0LjQxMzAwOTYzIEMzLjYxMTgyMzIxLDUuMjg5NjUyMDMgMi44MzgwNTcyLDUuNjEyMTc5NDkgMS44OTU4MTA0Myw1LjYxMjE3OTQ5IEMwLjkxNTcyOTEzNiw1LjYxMjE3OTQ5IDAsNS4yNTk5ODg5MiAwLDQuMDg4ODAwNiBMMCwzLjY4ODI0NzczIEwxLjM5OTQwODIzLDMuNjg4MjQ3NzMgTDEuNDAxMjE2MjUsNC4xOTIzMTg3OSBDMS40MDEyMTYyNSw0LjQ3ODY1ODYgMS41OTYwODA3Myw0LjY2OTI4Mjc1IDEuOTIxODU5MzIsNC42NjkyODI3NSBDMi4yNzA3NDA0LDQuNjY5MjgyNzUgMi4zODgzOTU2OSw0LjQ5MTUwNTg5IDIuNDMxMTg1NTIsNC4zMTU0MTA2MSBDMi40NTYyMjk5Niw0LjIxNjM5OTA1IDIuNDcxNDk3NjksNC4wNTQ2MzA4NSAyLjQyMTAwNzAzLDMuOTI4NjQ2NzEgQzIuMTUxMzQ0MDYsMy4yNTA5NjkxMSAwLjI5NzkyMTY3NywyLjk0MTI4ODk1IDAuMDcyMTE5OTQ3MywxLjg3MDMyMjkyIEMwLjAxNzM0MzYwODUsMS42MDU2NDE4OSAwLjAyMzgzOTA5MTIsMS4zOTkwNzYzNCAwLjA2MTc0MDU2NzcsMS4xNjQyNjAyMSBDMC4yMDAyMjE1ODEsMC4zMDg4NzMwMDcgMC45NTcxMTI3MjcsMCAxLjg4MzQyMjEzLDAgWiBNMTguODQxMTE4NiwwLjAyOTY2MzEwODkgQzE5LjU3MDQ4NzcsMC4wMjk2NjMxMDg5IDIwLjU3Nzg5MDEsMC4yMDY5NjkxMjkgMjAuNTc3ODkwMSwxLjMzNTY0NzA2IEwyMC41Nzc4OTAxLDEuNzAwNzUyMTcgTDE5LjI5MjE4NjQsMS43MDA3NTIxNyBMMTkuMjkyMTg2NCwxLjM4MDQ0NDQxIEMxOS4yOTIxODY0LDEuMDk3NDAwNSAxOS4xMTU2MDMsMC45MDg3OTQyNSAxOC44MTQ0MDAxLDAuOTA4Nzk0MjUgQzE4LjQ5MTIzMzEsMC45MDg3OTQyNSAxOC4zNzkwNjg4LDEuMDkxNjE1ODYgMTguMzQxNzcsMS4yNTkzNzA0OSBDMTguMzI5NzgzNSwxLjMxNjgxMzM0IDE4LjMyMzA4NzEsMS40MjY2NTQyOCAxOC4zNDYyNTY2LDEuNTMwMTcyNDggQzE4LjQ5NDMxMzQsMi4xMzY0MTY0NyAyMC40NTAzOTEyLDIuNDYzMTE0MjUgMjAuNjY2MDE0NCwzLjU0OTIxNDUyIEMyMC42ODk2NTI2LDMuNjU5MDU1NDcgMjAuNzM0MDQ5NiwzLjk1MDcwOTA3IDIwLjY3NTE4ODUsNC4zOTg0MTM1IEMyMC41NTE3NzQzLDUuMjY1MTAwOTMgMTkuNzgyNDk0OSw1LjU4NDgwMzMzIDE4Ljg1MTA5NjIsNS41ODQ4MDMzMyBDMTcuODc4NTgxOCw1LjU4NDgwMzMzIDE2Ljk3NTY0MjgsNS4yMzU0Mzc4MyAxNi45NzU2NDI4LDQuMDc3NTAwMzcgTDE2Ljk3NTY0MjgsMy42Nzc4ODkxOSBMMTguMzU5NTE1NCwzLjY3Nzg4OTE5IEwxOC4zNTk5MTcyLDQuMTgwNjE0OTggQzE4LjM1OTkxNzIsNC40NjMwNTM1MiAxOC41NTUxODM0LDQuNjUwNDQ5MDMgMTguODc5NzU2Nyw0LjY1MDQ0OTAzIEMxOS4yMjU3NTgzLDQuNjUwNDQ5MDMgMTkuMzQyNDc2MSw0LjQ3NTE2MDkxIDE5LjM4MjM4NjUsNC4zMDA4ODE3NCBDMTkuNDA2MzU5NSw0LjIwNTU2OTY2IDE5LjQxOTgxOTIsNC4wNDM4MDE0NiAxOS4zNzI4MTA3LDMuOTE2OTQyOSBDMTkuMTA2NDI4OSwzLjI0NzI2OTYzIDE3LjI3MTE1MzcsMi45NDAwNzgyMSAxNy4wNDc3NjI3LDEuODgxMTUyMyBDMTYuOTkwMTA2OSwxLjYxOTM2MzYgMTYuOTk5MDgwMSwxLjQxNDIxMDU4IDE3LjAzNTMwNzQsMS4xODMwOTM5MyBDMTcuMTcyMzE1MywwLjMzMzgyNzY4NiAxNy45MjI1MSwwLjAyOTY2MzEwODkgMTguODQxMTE4NiwwLjAyOTY2MzEwODkgWiBNMjMuMjM2NDg0NSwwLjE2Njg4MDIxMSBMMjMuMjM2MjI5MSw0LjExMTM3MzY2IEMyMy4yMzcyMDYzLDQuMTU3OTg0NjIgMjMuMjQwODgxOCw0LjIwNDA2NzQ1IDIzLjI0OTY3NjQsNC4yNDExNTE5NCBDMjMuMjc1MTIyNiw0LjM3MTIzOTEzIDIzLjM4Nzc1NTYsNC42MjE1OTMwOCAyMy43NDY5NDkxLDQuNjIxNTkzMDggQzI0LjExMDgzMDEsNC42MjE1OTMwOCAyNC4yMjA1ODM2LDQuMzcxMjM5MTMgMjQuMjQ4MTA1Nyw0LjI0MTE1MTk0IEMyNC4yNTk2OTA1LDQuMTg1NTI1MiAyNC4yNjE3NjYzLDQuMTA5NjUyMjIgMjQuMjU5NjkwNSw0LjA0MjExOTg4IEwyNC4yNTk2OTA1LDAuMTY2ODgwMjExIEwyNS41Nzg2MDgzLDAuMTY2ODgwMjExIEwyNS41Nzg2MDgzLDMuOTIxODUzMTIgQzI1LjU4NDMwMDIsNC4wMTg2NDQ5OSAyNS41NzQ3MjQ0LDQuMjE2Mzk5MDUgMjUuNTY3NDI1Myw0LjI2ODE5MTc4IEMyNS40NzQ3NDc1LDUuMjQ2NjcwNzkgMjQuNzA3NDc3LDUuNTY0MzU1MjkgMjMuNzQ2OTQ5MSw1LjU2NDM1NTI5IEMyMi43ODgyOTYyLDUuNTY0MzU1MjkgMjIuMDIwNTU3LDUuMjQ2NjcwNzkgMjEuOTI5NTUzMiw0LjI2ODE5MTc4IEMyMS45MjM4NjEzLDQuMjE2Mzk5MDUgMjEuOTE2Mjk0NCw0LjAxODY0NDk5IDIxLjkxNzk2ODUsMy45MjE4NTMxMiBMMjEuOTE3OTY4NSwwLjE2Njg4MDIxMSBMMjMuMjM2NDg0NSwwLjE2Njg4MDIxMSBaIE0zNC42Mjk3NjIxLDAuMDI1OTYzNjI4MiBDMzUuNTUzOTk1NiwwLjAyNTk2MzYyODIgMzYuMzYwMDM4MiwwLjMzNjg1NDUzNCAzNi40NTg3NDI3LDEuMzE5NTAzODcgQzM2LjQ2NTU3MywxLjM5MTE5NjkyIDM2LjQ2ODM4MTQsMS40NjUxMTM3OCAzNi40NjkzNjA3LDEuNTI2MTgwMjIgTDM2LjQ2OTAwNCwxLjY1NTc1NDAyIEwzNi40Njg3MjAzLDEuNjY2MTc4ODQgTDM2LjQ2ODcyMDMsMS44MzgwMzY1NCBMMzUuMTU1MzYwNSwxLjgzODAzNjU0IEwzNS4xNTUxNSwxLjUzMzU1NTU2IEMzNS4xNTQ0NDcxLDEuNDk4NzMzNjIgMzUuMTUxNDksMS40MDU2NDEyMyAzNS4xMzk0OTAxLDEuMzQ1MjY1NzEgQzM1LjExNjY1NTUsMS4yMzEzMjE3IDM1LjAxNzA4MDQsMC45NjYwMzUzMDYgMzQuNjE4MTc3NCwwLjk2NjAzNTMwNiBDMzQuMjM4MTU4MiwwLjk2NjAzNTMwNiAzNC4xMjU1OTIxLDEuMjE3NTk5OTkgMzQuMDk5Njc3MiwxLjM0NTI2NTcxIEMzNC4wODE3OTc4LDEuNDE1NDIxMzIgMzQuMDc2MTA1OSwxLjUwODExMDEyIDM0LjA3NjEwNTksMS41OTI3OTQ2IEwzNC4wNzYxMDU5LDMuOTg2ODk2NzIgQzM0LjA3NjEwNTksNC4wNTQ2MzA4NSAzNC4wODAxOTA3LDQuMTI3NjExNTEgMzQuMDg5NzY2NSw0LjE4NjEzMDU3IEMzNC4xMTI1MzQyLDQuMzI3NTE4IDM0LjI0Mzg1MDEsNC41NjgyNTMzIDM0LjYyMDk4OTksNC41NjgyNTMzIEMzNS4wMDA0MDY0LDQuNTY4MjUzMyAzNS4xMzQxMzMsNC4zMjc1MTggMzUuMTU1MzYwNSw0LjE4NjEzMDU3IEMzNS4xNjY5NDUyLDQuMTI3NjExNTEgMzUuMTcwNjI4Miw0LjA1NDYzMDg1IDM1LjE2ODk1NDEsMy45ODY4OTY3MiBMMzUuMTY4OTU0MSwzLjIyODkwNjc2IEwzNC42MzQ0NDk2LDMuMjI4OTA2NzYgTDM0LjYzNDQ0OTYsMi40NjQ5MzAzNiBMMzYuNDc5MTY2NywyLjQ2NDkzMDM2IEwzNi40NzkxNjY3LDMuODY4NTEzMzQgQzM2LjQ3NzA5MDgsMy45NjQ0MzA3OCAzNi40NzU0ODM3LDQuMDM4Njg5NDUgMzYuNDYwMDE1LDQuMjEzOTc3NTcgQzM2LjM3MjgyODIsNS4xNjk1ODcwNyAzNS41NTM5OTU2LDUuNTA4MzI0OTcgMzQuNjI2MDc5MSw1LjUwODMyNDk3IEMzMy43MDM4NTQ1LDUuNTA4MzI0OTcgMzIuODc5MzMsNS4xNjk1ODcwNyAzMi43OTM2MTY0LDQuMjEzOTc3NTcgQzMyLjc3NTkzOCw0LjAzODY4OTQ1IDMyLjc3Mjg1NzYsMy45NjQ0MzA3OCAzMi43NzI4NTc2LDMuODY4NTEzMzQgTDMyLjc3Mjg1NzYsMS42NjYxNzg4NCBDMzIuNzcyODU3NiwxLjU3MDI2MTQgMzIuNzg4NzI4LDEuNDA5MDk4NTcgMzIuNzk5MTA3NCwxLjMxOTUwMzg3IEMzMi45MTQxNTExLDAuMzM5NzQ2ODU1IDMzLjcwMzg1NDUsMC4wMjU5NjM2MjgyIDM0LjYyOTc2MjEsMC4wMjU5NjM2MjgyIFogTTEyLjE0NDc0NDcsMC4xNjY4ODAyMTEgTDEyLjgwMjI2MTYsNC4yNjE1OTk5OCBMMTMuNDYwMTgwNCwwLjE2Njg4MDIxMSBMMTUuNTg1Mjc0NiwwLjE2Njg4MDIxMSBMMTUuNzAxOTI1NSw1LjQwNTIxMDM2IEwxNC4zOTUyNjIsNS40MDUyMTAzNiBMMTQuMzU5ODM4MiwwLjU1NTkzMTA1NCBMMTMuNDYyMjU2Miw1LjQwNTIxMDM2IEwxMi4xMzk4NTYzLDUuNDA1MjEwMzYgTDExLjI0MzA3NzksMC41NTU5MzEwNTQgTDExLjIwNzc4OCw1LjQwNTIxMDM2IEw5LjkwNDQwNTgsNS40MDUyMTAzNiBMMTAuMDE3MjM5NywwLjE2Njg4MDIxMSBMMTIuMTQ0NzQ0NywwLjE2Njg4MDIxMSBaIE03LjkwMTI1MjUsMC4xNjY4ODAyMTEgTDguODYzMjUzNTgsNS40MDUyMTAzNiBMNy40NjQzMTQxLDUuNDA1MjEwMzYgTDYuNzUzODI4ODMsMC41NTU5MzEwNTQgTDYuMDI1ODY2MDIsNS40MDUyMTAzNiBMNC42MTcxNDk4Myw1LjQwNTIxMDM2IEw1LjU4MzE2ODczLDAuMTY2ODgwMjExIEw3LjkwMTI1MjUsMC4xNjY4ODAyMTEgWiBNMjguMzA0ODM2LDUuMzUwNTkyNTcgTDI3LjAyNDYyMzMsNS4zNTA1OTI1NyBMMjcuMDI0NjIzMywwLjE2Njg4MDIxMSBMMjguOTU5ODc1MywwLjE2Njg4MDIxMSBMMzAuMTg3OTkwMyw0LjM4NDM1NTQ3IEwzMC4xMTY4NzQ4LDAuMTY2ODgwMjExIEwzMS40MDU0NTgxLDAuMTY2ODgwMjExIEwzMS40MDU0NTgxLDUuMzUwNTkyNTcgTDI5LjU0OTQyNDEsNS4zNTA1OTI1NyBMMjguMjMsMC45OTkgTDI4LjMwNDgzNiw1LjM1MDU5MjU3IFoiPjwvcGF0aD48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iaTUiPjxwYXRoIGQ9Ik0yNC4zMzUyOTY2LDIuNDcwMDY0MzIgQzI1LjMwOTY1MDIsMi40NzAwNjQzMiAyNi4xMjEzMjMsMi42NTY2MDU2NCAyNi43NjkyNzY4LDMuMDI4OTczNTYgQzI3LjQxNzIzMDUsMy40MDE2OTg4MyAyNy45Mjk1MDE3LDMuOTA4NDMzNjcgMjguMzA2MDkwMiw0LjU1MDI1MDE1IEwyNi4zOTU0NTczLDUuNDgyNTk5MzcgQzI2LjE5NjA4NjksNS4xNDYzMjQ3IDI1LjkxOTE4MzYsNC44ODAwOTIzNiAyNS41NjQ3NDczLDQuNjg0NjE3MDggQzI1LjIxMDMxMTEsNC40ODkxNDE3OSAyNC44MDA0OTQyLDQuMzkxMjI1NDcgMjQuMzM1Mjk2Niw0LjM5MTIyNTQ3IEMyMy44MDM2NDIyLDQuMzkxMjI1NDcgMjMuNDEwNDM5NSw0LjQ5NDg1OTUzIDIzLjE1NTY4ODUsNC43MDIxMjc2NiBDMjIuOTAwNTkxMyw0LjkwOTM5NTc5IDIyLjc3MzU2MTksNS4xNDk1NDA5MyAyMi43NzM1NjE5LDUuNDIyMjA1NzMgQzIyLjc3MzU2MTksNS43Mzg0NjgzMSAyMi45NjE4NTYyLDUuOTY1MDMzODEgMjMuMzM4NDQ0Nyw2LjEwMDgzMDE3IEMyMy43MTQ2ODcsNi4yMzczNDEyNSAyNC4yNjg4Mzk4LDYuMzgyMDcxNTggMjQuOTk5ODY0Niw2LjUzNDY2MzgxIEMyNS4zOTg2MDU0LDYuNjExMTM4NiAyNS43OTk3NjksNi43MTE1NTY0NCAyNi4yMDQzOTQsNi44MzczNDY3NSBDMjYuNjA4NjcyOSw2Ljk2Mjc3OTcgMjYuOTc2OTU0Myw3LjEzMTgxMDQzIDI3LjMwOTIzODMsNy4zNDQ3OTYzMSBDMjcuNjQxNTIyMiw3LjU1NzQyNDgyIDI3LjkxMDExODUsNy44Mjk3MzIyNiAyOC4xMTUwMjY5LDguMTYyNzkwNyBDMjguMzE5OTM1NCw4LjQ5NTQ5MTc4IDI4LjQyMjM4OTYsOC45MTI1Mjk1NSAyOC40MjIzODk2LDkuNDE0MjYxMzcgQzI4LjQyMjM4OTYsOS43NTIzMjI4MyAyOC4zNDQ4NTY3LDEwLjEwNDMyMTMgMjguMTg5NzkwOCwxMC40Njk1NDIgQzI4LjAzNDM3ODgsMTAuODM0NzYyOCAyNy43OTM4MTkxLDExLjE3MzE4MTYgMjcuNDY3MDczMSwxMS40ODM3MjY0IEMyNy4xNDAzMjcyLDExLjc5NDk4NiAyNi43Mjc3NDEzLDEyLjA0ODM1MzQgMjYuMjI5MzE1MywxMi4yNDQ1NDM0IEMyNS43MzA4ODkzLDEyLjQ0MTA5MDggMjUuMTMyNzc4MiwxMi41MzkwMDcxIDI0LjQzNDk4MTgsMTIuNTM5MDA3MSBDMjMuMzYwNTk2OSwxMi41MzkwMDcxIDIyLjQ2ODk2ODIsMTIuMzMyODExIDIxLjc2MDA5NTcsMTEuOTIwMDYxNiBDMjEuMDUxMjIzMywxMS41MDY5NTQ4IDIwLjUwMjk1NDcsMTAuOTEwNTIyOCAyMC4xMTUyOSwxMC4xMzAwNTExIEwyMi4xOTIwNjQ5LDkuMTY1MTgyMjUgQzIyLjQyNDY2MzcsOS42MDQ3MzM2MyAyMi43NDAzMzM1LDkuOTQyNDM3NzQgMjMuMTM5MDc0MywxMC4xNzg2NTE5IEMyMy41Mzc4MTUxLDEwLjQxNDUwODggMjQuMDAzMDEyNiwxMC41MzIwNzk4IDI0LjUzNDY2NywxMC41MzIwNzk4IEMyNS4wODg0NzM2LDEwLjUzMjA3OTggMjUuNTAzODI4NiwxMC40MTc3MjUgMjUuNzgwNzMxOSwxMC4xODkwMTUzIEMyNi4wNTcyODkxLDkuOTU5OTQ4MzIgMjYuMTk2MDg2OSw5LjY4NzY0MDg4IDI2LjE5NjA4NjksOS4zNzEwMjA5NSBDMjYuMTk2MDg2OSw5LjE5NjYyOTgzIDI2LjEzMjM5OTIsOS4wNTUxMTU3MyAyNi4wMDUwMjM2LDguOTQ1NzYzOTIgQzI1Ljg3NzY0ODEsOC44MzcxMjY4MyAyNS43MTE1MDYxLDguNzQxMzU0NjYgMjUuNTA2NTk3Niw4LjY1OTg3Njg1IEMyNS4zMDEzNDMxLDguNTc4MDQxNjcgMjUuMDYwNzgzMyw4LjUxMDE0MzQ5IDI0Ljc4Mzg4LDguNDU1MTEwMjMgQzI0LjUwNjk3NjcsOC40MDA3OTE2OSAyNC4yMTg5OTcyLDguMzQwNzU1NCAyMy45MTk5NDE2LDguMjc1MzU4NzMgQzIzLjQ5ODcwMjUsOC4xODgxNjMxOCAyMy4wODY0NjI2LDguMDg0ODg2NDcgMjIuNjgyMTgzOCw3Ljk2NDgxMzkgQzIyLjI3NzkwNSw3Ljg0NTA5ODY5IDIxLjkxNTE2MTYsNy42Nzg1Njk0NyAyMS41OTM5NTM4LDcuNDY2Mjk4MzEgQzIxLjI3Mjc0NTksNy4yNTMzMTI0NCAyMS4wMTI0NTY4LDYuOTgwNjQ3NjQgMjAuODEzMDg2NCw2LjY0ODMwMzkyIEMyMC42MTM3MTYsNi4zMTU5NjAyIDIwLjUxNDAzMDgsNS44OTg5MjI0MyAyMC41MTQwMzA4LDUuMzk3NTQ3OTcgQzIwLjUxNDAzMDgsNS4wMTU1MzEzNyAyMC42MDU0MDg5LDQuNjQ3ODA5MTIgMjAuNzg4MTY1MSw0LjI5MzY2NjUgQzIwLjk3MDkyMTMsMy45MzkxNjY1MyAyMS4yMjg0NDE0LDMuNjI2MTIwMTggMjEuNTYwNzI1NCwzLjM1MzA5ODAzIEMyMS44OTMwMDkzLDMuMDgwNzkwNTkgMjIuMjk0MTczLDIuODY1NjYwNTYgMjIuNzY1MjU0OCwyLjcwNzM1MDYgQzIzLjIzNTk5MDQsMi41NDkwNDA2MyAyMy43NTkzMzc3LDIuNDcwMDY0MzIgMjQuMzM1Mjk2NiwyLjQ3MDA2NDMyIFogTTMzLjEwNzM1MTUsMi40NzAwNjQzMiBDMzQuMDgxNzA1LDIuNDcwMDY0MzIgMzQuODkzMzc3OSwyLjY1NjYwNTY0IDM1LjU0MTMzMTYsMy4wMjg5NzM1NiBDMzYuMTg5Mjg1NCwzLjQwMTY5ODgzIDM2LjcwMTU1NjUsMy45MDg0MzM2NyAzNy4wNzgxNDUxLDQuNTUwMjUwMTUgTDM1LjE2NzUxMjIsNS40ODI1OTkzNyBDMzQuOTY4MTQxOCw1LjE0NjMyNDcgMzQuNjkxMjM4NCw0Ljg4MDA5MjM2IDM0LjMzNjgwMjIsNC42ODQ2MTcwOCBDMzMuOTgyMzY1OSw0LjQ4OTE0MTc5IDMzLjU3MjU0OSw0LjM5MTIyNTQ3IDMzLjEwNzM1MTUsNC4zOTEyMjU0NyBDMzIuNTc1Njk3MSw0LjM5MTIyNTQ3IDMyLjE4MjQ5NDQsNC40OTQ4NTk1MyAzMS45Mjc3NDMzLDQuNzAyMTI3NjYgQzMxLjY3MjY0NjEsNC45MDkzOTU3OSAzMS41NDU2MTY3LDUuMTQ5NTQwOTMgMzEuNTQ1NjE2Nyw1LjQyMjIwNTczIEMzMS41NDU2MTY3LDUuNzM4NDY4MzEgMzEuNzMzOTExLDUuOTY1MDMzODEgMzIuMTEwNDk5NSw2LjEwMDgzMDE3IEMzMi40ODY3NDE5LDYuMjM3MzQxMjUgMzMuMDQwODk0Nyw2LjM4MjA3MTU4IDMzLjc3MTkxOTQsNi41MzQ2NjM4MSBDMzQuMTcwNjYwMiw2LjYxMTEzODYgMzQuNTcxODIzOSw2LjcxMTU1NjQ0IDM0Ljk3NjQ0ODksNi44MzczNDY3NSBDMzUuMzgwNzI3Nyw2Ljk2Mjc3OTcgMzUuNzQ5MDA5MSw3LjEzMTgxMDQzIDM2LjA4MTI5MzEsNy4zNDQ3OTYzMSBDMzYuNDEzNTc3MSw3LjU1NzQyNDgyIDM2LjY4MjE3MzMsNy44Mjk3MzIyNiAzNi44ODcwODE4LDguMTYyNzkwNyBDMzcuMDkxOTkwMiw4LjQ5NTQ5MTc4IDM3LjE5NDQ0NDQsOC45MTI1Mjk1NSAzNy4xOTQ0NDQ0LDkuNDE0MjYxMzcgQzM3LjE5NDQ0NDQsOS43NTIzMjI4MyAzNy4xMTY5MTE1LDEwLjEwNDMyMTMgMzYuOTYxODQ1NywxMC40Njk1NDIgQzM2LjgwNjQzMzcsMTAuODM0NzYyOCAzNi41NjU4NzM5LDExLjE3MzE4MTYgMzYuMjM5MTI4LDExLjQ4MzcyNjQgQzM1LjkxMjM4MjEsMTEuNzk0OTg2IDM1LjQ5OTc5NjEsMTIuMDQ4MzUzNCAzNS4wMDEzNzAyLDEyLjI0NDU0MzQgQzM0LjUwMjk0NDIsMTIuNDQxMDkwOCAzMy45MDQ4MzMsMTIuNTM5MDA3MSAzMy4yMDcwMzY3LDEyLjUzOTAwNzEgQzMyLjEzMjY1MTgsMTIuNTM5MDA3MSAzMS4yNDEwMjMxLDEyLjMzMjgxMSAzMC41MzIxNTA2LDExLjkyMDA2MTYgQzI5LjgyMzI3ODEsMTEuNTA2OTU0OCAyOS4yNzUwMDk1LDEwLjkxMDUyMjggMjguODg3MzQ0OSwxMC4xMzAwNTExIEwzMC45NjQxMTk4LDkuMTY1MTgyMjUgQzMxLjE5NjcxODYsOS42MDQ3MzM2MyAzMS41MTIzODgzLDkuOTQyNDM3NzQgMzEuOTExMTI5MSwxMC4xNzg2NTE5IEMzMi4zMDk4Njk5LDEwLjQxNDUwODggMzIuNzc1MDY3NSwxMC41MzIwNzk4IDMzLjMwNjcyMTgsMTAuNTMyMDc5OCBDMzMuODYwNTI4NSwxMC41MzIwNzk4IDM0LjI3NTg4MzUsMTAuNDE3NzI1IDM0LjU1Mjc4NjgsMTAuMTg5MDE1MyBDMzQuODI5MzQ0LDkuOTU5OTQ4MzIgMzQuOTY4MTQxOCw5LjY4NzY0MDg4IDM0Ljk2ODE0MTgsOS4zNzEwMjA5NSBDMzQuOTY4MTQxOCw5LjE5NjYyOTgzIDM0LjkwNDQ1NCw5LjA1NTExNTczIDM0Ljc3NzA3ODUsOC45NDU3NjM5MiBDMzQuNjQ5NzAyOSw4LjgzNzEyNjgzIDM0LjQ4MzU2MSw4Ljc0MTM1NDY2IDM0LjI3ODY1MjUsOC42NTk4NzY4NSBDMzQuMDczMzk3OSw4LjU3ODA0MTY3IDMzLjgzMjgzODIsOC41MTAxNDM0OSAzMy41NTU5MzQ4LDguNDU1MTEwMjMgQzMzLjI3OTAzMTUsOC40MDA3OTE2OSAzMi45OTEwNTIxLDguMzQwNzU1NCAzMi42OTE5OTY1LDguMjc1MzU4NzMgQzMyLjI3MDc1NzMsOC4xODgxNjMxOCAzMS44NTg1MTc1LDguMDg0ODg2NDcgMzEuNDU0MjM4Niw3Ljk2NDgxMzkgQzMxLjA0OTk1OTgsNy44NDUwOTg2OSAzMC42ODcyMTY1LDcuNjc4NTY5NDcgMzAuMzY2MDA4Niw3LjQ2NjI5ODMxIEMzMC4wNDQ4MDA4LDcuMjUzMzEyNDQgMjkuNzg0NTExNiw2Ljk4MDY0NzY0IDI5LjU4NTE0MTIsNi42NDgzMDM5MiBDMjkuMzg1NzcwOSw2LjMxNTk2MDIgMjkuMjg2MDg1Nyw1Ljg5ODkyMjQzIDI5LjI4NjA4NTcsNS4zOTc1NDc5NyBDMjkuMjg2MDg1Nyw1LjAxNTUzMTM3IDI5LjM3NzQ2MzgsNC42NDc4MDkxMiAyOS41NjAyMTk5LDQuMjkzNjY2NSBDMjkuNzQyOTc2MSwzLjkzOTE2NjUzIDMwLjAwMDQ5NjIsMy42MjYxMjAxOCAzMC4zMzI3ODAyLDMuMzUzMDk4MDMgQzMwLjY2NTA2NDIsMy4wODA3OTA1OSAzMS4wNjYyMjc5LDIuODY1NjYwNTYgMzEuNTM3MzA5NiwyLjcwNzM1MDYgQzMyLjAwODA0NTMsMi41NDkwNDA2MyAzMi41MzEzOTI2LDIuNDcwMDY0MzIgMzMuMTA3MzUxNSwyLjQ3MDA2NDMyIFogTTEzLjc3MzIwNTcsMi40NzAwMjg1OSBDMTQuNDE1NjIxNCwyLjQ3MDAyODU5IDE1LjAwODE5NDUsMi41OTAxMDExNiAxNS41NTA5MjUsMi44Mjk1MzE1OSBDMTYuMDkzMzA5NCwzLjA2OTY3NjczIDE2LjU0MjIzODksMy4zOTY2NjAwNyAxNi44OTY2NzUxLDMuODEwODM4OTcgTDE2Ljg5NjY3NTEsMi40ODcxODE4MSBMMTkuMTM5NTkyLDIuNDg3MTgxODEgTDE5LjEzOTU5MiwxMi41MjE4MTgxIEwxNi44OTY2NzUxLDEyLjUyMTgxODEgTDE2Ljg5NjY3NTEsMTEuMDk1OTU2MyBDMTYuNTQyMjM4OSwxMS41NDQwODQzIDE2LjA4ODExNzQsMTEuODk2Nzk3NSAxNS41MzQzMTA4LDEyLjE1MzczODUgQzE0Ljk4MDUwNDIsMTIuNDEwNjc5NSAxNC4zODIzOTMsMTIuNTM4OTcxNCAxMy43Mzk5NzczLDEyLjUzODk3MTQgQzEzLjE1Mjk0MjMsMTIuNTM4OTcxNCAxMi41NzQyMTQzLDEyLjQyNjQwMzMgMTIuMDAzNzkzNSwxMi4yMDA1NTI1IEMxMS40MzMzNzI2LDExLjk3NDM0NDQgMTAuOTIxMTAxNSwxMS42NDYyODkgMTAuNDY2OTgwMSwxMS4yMTYzODYzIEMxMC4wMTI4NTg2LDEwLjc4NjQ4MzYgOS42NDcwMDAxMSwxMC4yNjAwOTQgOS4zNzA0NDI5Miw5LjYzNzU3NDkxIEM5LjA5MzUzOTYsOS4wMTQ2OTg0NCA4Ljk1NTA4Nzk0LDguMzA2NDEzMjIgOC45NTUwODc5NCw3LjUxMzA3NjU4IEM4Ljk1NTA4Nzk0LDYuNzA4MzA0NDcgOS4wOTA0MjQ0NCw1Ljk5NTAxNjIyIDkuMzYyMTM1ODIsNS4zNzE3ODI0IEM5LjYzMzUwMTA3LDQuNzQ4OTA1OTMgOS45OTM0NzUzOSw0LjIyMjg3MzcxIDEwLjQ0MjA1ODgsMy43OTI5NzEwMyBDMTAuODkwNjQyMSwzLjM2MjcxMDk4IDExLjQwNTY4MjMsMy4wMzUwMTI5MiAxMS45ODcxNzkzLDIuODA5NTE5NDkgQzEyLjU2ODY3NjMsMi41ODMzMTEzNCAxMy4xNjQwMTg0LDIuNDcwMDI4NTkgMTMuNzczMjA1NywyLjQ3MDAyODU5IFogTTQuMTUzNTQ5NzgsMCBDNC43ODQ4ODkzNSwwIDUuMzY2Mzg2MzIsMC4xMTcyMTM3MDEgNS44OTgwNDA2OSwwLjM1MTk5ODQ2MSBDNi40Mjk2OTUwNiwwLjU4NjQyNTg2MiA2Ljg4OTAwODQ0LDAuOTAzNDAzMTU2IDcuMjc3MDE5MjIsMS4zMDQwMDI0MiBDNy42NjQ2ODM4NiwxLjcwNDI0NDMyIDcuOTY4OTMxMzgsMi4xNzU5NTggOC4xOTA4MDAxNywyLjcxOTE0MzQ0IEM4LjQxMjMyMjgyLDMuMjYxOTcxNTIgOC41MjMwODQxNSwzLjg0MjMyMjI4IDguNTIzMDg0MTUsNC40NjAxOTU3MiBDOC41MjMwODQxNSw1LjA3NzM1NDQ0IDguNDEyMzIyODIsNS42NjA1NjQwOCA4LjE5MDgwMDE3LDYuMjA5NDY3MjYgQzcuOTY4OTMxMzgsNi43NTgzNzA0NCA3LjY2NDY4Mzg2LDcuMjMyOTQyOTkgNy4yNzcwMTkyMiw3LjYzMzU0MjI1IEM2Ljg4OTAwODQ0LDguMDMzNzg0MTYgNi40MjY5MjYwMyw4LjM1MTExODgxIDUuODg5NzMzNTksOC41ODUxODg4NSBDNS4zNTIxOTUwMiw4LjgxOTYxNjI1IDQuNzY4Mjc1MTUsOC45MzY4Mjk5NSA0LjEzNjkzNTU4LDguOTM2ODI5OTUgTDIuMjU5NTMxMDgsOC45MzY4Mjk5NSBMMi4yNTk1MzEwOCwxMi41MjE4NTM5IEwwLDEyLjUyMTg1MzkgTDAsMCBMNC4xNTM1NDk3OCwwIFogTTE0LjEwNTQ4OTcsNC41ODAyMzI1NiBDMTMuNjk1NjcyOCw0LjU4MDIzMjU2IDEzLjMxMDc3NzEsNC42NTU2MzUyNyAxMi45NTA4MDI4LDQuODA1NzI1OTkgQzEyLjU5MDgyODUsNC45NTU4MTY3IDEyLjI3NzkyNzgsNS4xNjAyMjU5NiAxMi4wMTIxMDA2LDUuNDE3NTI0MzMgQzExLjc0NjI3MzQsNS42NzU1Mzc0MSAxMS41Mzg1OTU5LDUuOTgxNzkzOTQgMTEuMzg5MDY4MSw2LjMzNjI5MzkxIEMxMS4yMzk1NDAzLDYuNjkwNzkzODkgMTEuMTY0Nzc2NCw3LjA3MTczODQxIDExLjE2NDc3NjQsNy40ODAxOTk1NyBDMTEuMTY0Nzc2NCw3Ljg4ODMwMzM3IDExLjIzOTU0MDMsOC4yNzI0NjQxMyAxMS4zODkwNjgxLDguNjMxOTY3MTIgQzExLjUzODU5NTksOC45OTE0NzAxMiAxMS43NDYyNzM0LDkuMzAyNzI5NjcgMTIuMDEyMTAwNiw5LjU2NjQ2MDUgQzEyLjI3NzkyNzgsOS44Mjk0NzY2MSAxMi41OTA4Mjg1LDEwLjAzNjAzIDEyLjk1MDgwMjgsMTAuMTg2ODM1NCBDMTMuMzEwNzc3MSwxMC4zMzY5MjYyIDEzLjY5NTY3MjgsMTAuNDExOTcxNSAxNC4xMDU0ODk3LDEwLjQxMTk3MTUgQzE0LjUyNjM4MjcsMTAuNDExOTcxNSAxNC45MTM3MDEyLDEwLjMzNjkyNjIgMTUuMjY4NDgzNiwxMC4xODY4MzU0IEMxNS42MjI5MTk5LDEwLjAzNjAzIDE1LjkyNzE2NzQsOS44MjY2MTc3NCAxNi4xODIyNjQ2LDkuNTU3ODgzODkgQzE2LjQzNzAxNTYsOS4yODk1MDczOSAxNi42MzkxNTUsOC45Nzc4OTA0OCAxNi43ODg2ODI4LDguNjIzNzQ3ODcgQzE2LjkzODIxMDYsOC4yNjk2MDUyNiAxNy4wMTI5NzQ1LDcuODg4MzAzMzcgMTcuMDEyOTc0NSw3LjQ4MDE5OTU3IEMxNy4wMTI5NzQ1LDcuMDgyODE2NTQgMTYuOTM4MjEwNiw2LjcwNjg3NTAzIDE2Ljc4ODY4MjgsNi4zNTIzNzUwNiBDMTYuNjM5MTU1LDUuOTk3ODc1MDkgMTYuNDM3MDE1Niw1LjY4OTExNzA1IDE2LjE4MjI2NDYsNS40MjYxMDA5NCBDMTUuOTI3MTY3NCw1LjE2MjcyNzQ3IDE1LjYyMjkxOTksNC45NTU4MTY3IDE1LjI2ODQ4MzYsNC44MDU3MjU5OSBDMTQuOTEzNzAxMiw0LjY1NTYzNTI3IDE0LjUyNjM4MjcsNC41ODAyMzI1NiAxNC4xMDU0ODk3LDQuNTgwMjMyNTYgWiBNMy45ODc0MDc3OSwyLjE2MTMwNjI4IEwyLjI1OTUzMTA4LDIuMTYxMzA2MjggTDIuMjU5NTMxMDgsNi43NzU1MjM2NyBMMy45ODc0MDc3OSw2Ljc3NTUyMzY3IEM0LjMxOTY5MTc3LDYuNzc1NTIzNjcgNC42MjQyODU0Miw2LjcxNTQ4NzM4IDQuOTAxMTg4NzQsNi41OTU0MTQ4MSBDNS4xNzc3NDU5Myw2LjQ3NTM0MjI0IDUuNDE2MjI4OTIsNi4zMDk1Mjc3NCA1LjYxNTU5OTMsNi4wOTgzMjg2NiBDNS44MTQ5Njk2OSw1Ljg4Njc3MjIyIDUuOTcwMDM1NTUsNS42NDA1NTE5OCA2LjA4MDc5Njg4LDUuMzYwMzgyNjUgQzYuMTkxMjEyMDgsNS4wODA1NzA2NyA2LjI0NjkzODg3LDQuNzgwMDMxODkgNi4yNDY5Mzg4Nyw0LjQ2MDE5NTcyIEM2LjI0NjkzODg3LDQuMTM5NjQ0ODQgNi4xOTEyMTIwOCwzLjgzOTQ2MzQxIDYuMDgwNzk2ODgsMy41NTkyOTQwOCBDNS45NzAwMzU1NSwzLjI3OTEyNDc1IDUuODE0OTY5NjksMy4wMzYxMjA3MyA1LjYxNTU5OTMsMi44MzAyODIwNCBDNS40MTYyMjg5MiwyLjYyNDQ0MzM0IDUuMTc3NzQ1OTMsMi40NjE0ODc3MSA0LjkwMTE4ODc0LDIuMzQxNDE1MTQgQzQuNjI0Mjg1NDIsMi4yMjEzNDI1NyA0LjMxOTY5MTc3LDIuMTYxMzA2MjggMy45ODc0MDc3OSwyLjE2MTMwNjI4IFoiPjwvcGF0aD48L2NsaXBQYXRoPjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMzLjAgLTE2MC4wKSI+PGcgY2xpcC1wYXRoPSJ1cmwoI2kwKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjMzLjAgMTYwLjApIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC4wMDAxODA1NTU1NTU1NTIwNzU0OCAwLjApIj48ZyBjbGlwLXBhdGg9InVybCgjaTEpIj48cG9seWdvbiBwb2ludHM9IjAsMCA1MiwwIDUyLDUyIDAsNTIgMCwwIiBzdHJva2U9Im5vbmUiIGZpbGw9InVybCgjaTIpIj48L3BvbHlnb24+PC9nPjwvZz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3LjU4MzMzMzMzMzMzMzMzMiAxNC44MDU1NTU1NTU1NTU1NSkiPjxnIGNsaXAtcGF0aD0idXJsKCNpMykiPjxnIGNsaXAtcGF0aD0idXJsKCNpNCkiPjxwb2x5Z29uIHBvaW50cz0iMCwwIDM2LjQ3OTE2NjcsMCAzNi40NzkxNjY3LDUuNjEyMTc5NDkgMCw1LjYxMjE3OTQ5IDAsMCIgc3Ryb2tlPSJub25lIiBmaWxsPSIjRkZGRkZGIj48L3BvbHlnb24+PC9nPjwvZz48L2c+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNy41ODMzMzMzMzMzMzMzNzEgMjQuNDMyMDgwMjU1NDc3MzMpIj48ZyBjbGlwLXBhdGg9InVybCgjaTUpIj48cG9seWdvbiBwb2ludHM9IjAsMCAzNy4xOTQ0NDQ0LDAgMzcuMTk0NDQ0NCwxMi41MzkwMDcxIDAsMTIuNTM5MDA3MSAwLDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0iI0ZGRkZGRiI+PC9wb2x5Z29uPjwvZz48L2c+PC9nPjwvZz48L2c+PC9zdmc+\",\n    \"icon_light\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgd2lkdGg9IjUycHgiIGhlaWdodD0iNTJweCIgdmlld0JveD0iMCAwIDUyLjAgNTIuMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGRlZnM+PGNsaXBQYXRoIGlkPSJpMCI+PHBhdGggZD0iTTM2MCwwIEwzNjAsODAwIEwwLDgwMCBMMCwwIEwzNjAsMCBaIj48L3BhdGg+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImkxIj48cGF0aCBkPSJNMjYsMCBDMzMuOTkxMDI3OCwwIDQxLjEzOTU4MzMsMC45NzUgNDUuOTA4Nzc3OCw1Ljc3Nzc3Nzc4IEM0OS43MTAxOTQ0LDkuNjA1OTE2NjcgNTIsMTUuODY1MDU1NiA1MiwyNiBDNTIsMzYuMTM0OTQ0NCA0OS43MDk4MzMzLDQyLjM5NDQ0NDQgNDUuOTA4MDU1Niw0Ni4yMjI1ODMzIEM0MS4xMzg4NjExLDUxLjAyNDYzODkgMzMuOTkwMzA1Niw1MiAyNiw1MiBDMTguMDA4OTcyMiw1MiAxMC44NjA3Nzc4LDUxLjAyNDYzODkgNi4wOTE1ODMzMyw0Ni4yMjI1ODMzIEMyLjI5MDE2NjY3LDQyLjM5NDQ0NDQgMCwzNi4xMzQ5NDQ0IDAsMjYgQzAsMTUuODY1MDU1NiAyLjI4OTgwNTU2LDkuNjA1NTU1NTYgNi4wOTA4NjExMSw1Ljc3Nzc3Nzc4IEMxMC44NjAwNTU2LDAuOTc1IDE4LjAwODYxMTEsMCAyNiwwIFoiPjwvcGF0aD48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iaTIiIHgxPSIyNnB4IiB5MT0iNTJweCIgeDI9IjI2cHgiIHkyPSIwLjE5NTkyMTE0OHB4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iIzI5MjlCMiIgb2Zmc2V0PSIwJSI+PC9zdG9wPjxzdG9wIHN0b3AtY29sb3I9IiMxQTQwQ0MiIG9mZnNldD0iMTAwJSI+PC9zdG9wPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJpMyI+PHBhdGggZD0iTTM3LjE5NDQ0NDQsMCBMMzcuMTk0NDQ0NCw1LjcyMjIyMjIyIEwwLDUuNzIyMjIyMjIgTDAsMCBMMzcuMTk0NDQ0NCwwIFoiPjwvcGF0aD48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iaTQiPjxwYXRoIGQ9Ik0xLjg4MzQyMjEzLDAgQzIuNjIwNzU5ODcsMCAzLjY0MzU2NDAyLDAuMTgxNjEwODcxIDMuNjQzNTY0MDIsMS4zMjExMTgxOSBMMy42NDM1NjQwMiwxLjY4OTExNTYyIEwyLjM0Mjc5MzM5LDEuNjg5MTE1NjIgTDIuMzQyNzkzMzksMS4zNjQ5MDY1OSBDMi4zNDI3OTMzOSwxLjA3OTU3NTczIDIuMTYzMTk2NjQsMC44ODkxNTMzNzEgMS44NTgxNzY4LDAuODg5MTUzMzcxIEMxLjUyOTMxNzg4LDAuODg5MTUzMzcxIDEuNDE2NDgzOTgsMS4wNzUyNzA4OCAxLjM4MDg1OTI3LDEuMjQyMTUxMDkgQzEuMzY2Nzk2ODgsMS4zMDAyNjY1NyAxLjM2MDkwNDA4LDEuNDExNzIxODQgMS4zODYxNDk0MSwxLjUxNzI1NzkzIEMxLjUzNDYwODAyLDIuMTMwNDk3MyAzLjUxMjQ0OTAyLDIuNDU2MTE4ODcgMy43MzI1NTg4MywzLjU1NTUzNzI3IEMzLjc1MzcxOTM3LDMuNjY3MDU5OCAzLjgwMDc5NDg4LDMuOTYxMTM0ODggMy43MzgwNDk4Niw0LjQxMzAwOTYzIEMzLjYxMTgyMzIxLDUuMjg5NjUyMDMgMi44MzgwNTcyLDUuNjEyMTc5NDkgMS44OTU4MTA0Myw1LjYxMjE3OTQ5IEMwLjkxNTcyOTEzNiw1LjYxMjE3OTQ5IDAsNS4yNTk5ODg5MiAwLDQuMDg4ODAwNiBMMCwzLjY4ODI0NzczIEwxLjM5OTQwODIzLDMuNjg4MjQ3NzMgTDEuNDAxMjE2MjUsNC4xOTIzMTg3OSBDMS40MDEyMTYyNSw0LjQ3ODY1ODYgMS41OTYwODA3Myw0LjY2OTI4Mjc1IDEuOTIxODU5MzIsNC42NjkyODI3NSBDMi4yNzA3NDA0LDQuNjY5MjgyNzUgMi4zODgzOTU2OSw0LjQ5MTUwNTg5IDIuNDMxMTg1NTIsNC4zMTU0MTA2MSBDMi40NTYyMjk5Niw0LjIxNjM5OTA1IDIuNDcxNDk3NjksNC4wNTQ2MzA4NSAyLjQyMTAwNzAzLDMuOTI4NjQ2NzEgQzIuMTUxMzQ0MDYsMy4yNTA5NjkxMSAwLjI5NzkyMTY3NywyLjk0MTI4ODk1IDAuMDcyMTE5OTQ3MywxLjg3MDMyMjkyIEMwLjAxNzM0MzYwODUsMS42MDU2NDE4OSAwLjAyMzgzOTA5MTIsMS4zOTkwNzYzNCAwLjA2MTc0MDU2NzcsMS4xNjQyNjAyMSBDMC4yMDAyMjE1ODEsMC4zMDg4NzMwMDcgMC45NTcxMTI3MjcsMCAxLjg4MzQyMjEzLDAgWiBNMTguODQxMTE4NiwwLjAyOTY2MzEwODkgQzE5LjU3MDQ4NzcsMC4wMjk2NjMxMDg5IDIwLjU3Nzg5MDEsMC4yMDY5NjkxMjkgMjAuNTc3ODkwMSwxLjMzNTY0NzA2IEwyMC41Nzc4OTAxLDEuNzAwNzUyMTcgTDE5LjI5MjE4NjQsMS43MDA3NTIxNyBMMTkuMjkyMTg2NCwxLjM4MDQ0NDQxIEMxOS4yOTIxODY0LDEuMDk3NDAwNSAxOS4xMTU2MDMsMC45MDg3OTQyNSAxOC44MTQ0MDAxLDAuOTA4Nzk0MjUgQzE4LjQ5MTIzMzEsMC45MDg3OTQyNSAxOC4zNzkwNjg4LDEuMDkxNjE1ODYgMTguMzQxNzcsMS4yNTkzNzA0OSBDMTguMzI5NzgzNSwxLjMxNjgxMzM0IDE4LjMyMzA4NzEsMS40MjY2NTQyOCAxOC4zNDYyNTY2LDEuNTMwMTcyNDggQzE4LjQ5NDMxMzQsMi4xMzY0MTY0NyAyMC40NTAzOTEyLDIuNDYzMTE0MjUgMjAuNjY2MDE0NCwzLjU0OTIxNDUyIEMyMC42ODk2NTI2LDMuNjU5MDU1NDcgMjAuNzM0MDQ5NiwzLjk1MDcwOTA3IDIwLjY3NTE4ODUsNC4zOTg0MTM1IEMyMC41NTE3NzQzLDUuMjY1MTAwOTMgMTkuNzgyNDk0OSw1LjU4NDgwMzMzIDE4Ljg1MTA5NjIsNS41ODQ4MDMzMyBDMTcuODc4NTgxOCw1LjU4NDgwMzMzIDE2Ljk3NTY0MjgsNS4yMzU0Mzc4MyAxNi45NzU2NDI4LDQuMDc3NTAwMzcgTDE2Ljk3NTY0MjgsMy42Nzc4ODkxOSBMMTguMzU5NTE1NCwzLjY3Nzg4OTE5IEwxOC4zNTk5MTcyLDQuMTgwNjE0OTggQzE4LjM1OTkxNzIsNC40NjMwNTM1MiAxOC41NTUxODM0LDQuNjUwNDQ5MDMgMTguODc5NzU2Nyw0LjY1MDQ0OTAzIEMxOS4yMjU3NTgzLDQuNjUwNDQ5MDMgMTkuMzQyNDc2MSw0LjQ3NTE2MDkxIDE5LjM4MjM4NjUsNC4zMDA4ODE3NCBDMTkuNDA2MzU5NSw0LjIwNTU2OTY2IDE5LjQxOTgxOTIsNC4wNDM4MDE0NiAxOS4zNzI4MTA3LDMuOTE2OTQyOSBDMTkuMTA2NDI4OSwzLjI0NzI2OTYzIDE3LjI3MTE1MzcsMi45NDAwNzgyMSAxNy4wNDc3NjI3LDEuODgxMTUyMyBDMTYuOTkwMTA2OSwxLjYxOTM2MzYgMTYuOTk5MDgwMSwxLjQxNDIxMDU4IDE3LjAzNTMwNzQsMS4xODMwOTM5MyBDMTcuMTcyMzE1MywwLjMzMzgyNzY4NiAxNy45MjI1MSwwLjAyOTY2MzEwODkgMTguODQxMTE4NiwwLjAyOTY2MzEwODkgWiBNMjMuMjM2NDg0NSwwLjE2Njg4MDIxMSBMMjMuMjM2MjI5MSw0LjExMTM3MzY2IEMyMy4yMzcyMDYzLDQuMTU3OTg0NjIgMjMuMjQwODgxOCw0LjIwNDA2NzQ1IDIzLjI0OTY3NjQsNC4yNDExNTE5NCBDMjMuMjc1MTIyNiw0LjM3MTIzOTEzIDIzLjM4Nzc1NTYsNC42MjE1OTMwOCAyMy43NDY5NDkxLDQuNjIxNTkzMDggQzI0LjExMDgzMDEsNC42MjE1OTMwOCAyNC4yMjA1ODM2LDQuMzcxMjM5MTMgMjQuMjQ4MTA1Nyw0LjI0MTE1MTk0IEMyNC4yNTk2OTA1LDQuMTg1NTI1MiAyNC4yNjE3NjYzLDQuMTA5NjUyMjIgMjQuMjU5NjkwNSw0LjA0MjExOTg4IEwyNC4yNTk2OTA1LDAuMTY2ODgwMjExIEwyNS41Nzg2MDgzLDAuMTY2ODgwMjExIEwyNS41Nzg2MDgzLDMuOTIxODUzMTIgQzI1LjU4NDMwMDIsNC4wMTg2NDQ5OSAyNS41NzQ3MjQ0LDQuMjE2Mzk5MDUgMjUuNTY3NDI1Myw0LjI2ODE5MTc4IEMyNS40NzQ3NDc1LDUuMjQ2NjcwNzkgMjQuNzA3NDc3LDUuNTY0MzU1MjkgMjMuNzQ2OTQ5MSw1LjU2NDM1NTI5IEMyMi43ODgyOTYyLDUuNTY0MzU1MjkgMjIuMDIwNTU3LDUuMjQ2NjcwNzkgMjEuOTI5NTUzMiw0LjI2ODE5MTc4IEMyMS45MjM4NjEzLDQuMjE2Mzk5MDUgMjEuOTE2Mjk0NCw0LjAxODY0NDk5IDIxLjkxNzk2ODUsMy45MjE4NTMxMiBMMjEuOTE3OTY4NSwwLjE2Njg4MDIxMSBMMjMuMjM2NDg0NSwwLjE2Njg4MDIxMSBaIE0zNC42Mjk3NjIxLDAuMDI1OTYzNjI4MiBDMzUuNTUzOTk1NiwwLjAyNTk2MzYyODIgMzYuMzYwMDM4MiwwLjMzNjg1NDUzNCAzNi40NTg3NDI3LDEuMzE5NTAzODcgQzM2LjQ2NTU3MywxLjM5MTE5NjkyIDM2LjQ2ODM4MTQsMS40NjUxMTM3OCAzNi40NjkzNjA3LDEuNTI2MTgwMjIgTDM2LjQ2OTAwNCwxLjY1NTc1NDAyIEwzNi40Njg3MjAzLDEuNjY2MTc4ODQgTDM2LjQ2ODcyMDMsMS44MzgwMzY1NCBMMzUuMTU1MzYwNSwxLjgzODAzNjU0IEwzNS4xNTUxNSwxLjUzMzU1NTU2IEMzNS4xNTQ0NDcxLDEuNDk4NzMzNjIgMzUuMTUxNDksMS40MDU2NDEyMyAzNS4xMzk0OTAxLDEuMzQ1MjY1NzEgQzM1LjExNjY1NTUsMS4yMzEzMjE3IDM1LjAxNzA4MDQsMC45NjYwMzUzMDYgMzQuNjE4MTc3NCwwLjk2NjAzNTMwNiBDMzQuMjM4MTU4MiwwLjk2NjAzNTMwNiAzNC4xMjU1OTIxLDEuMjE3NTk5OTkgMzQuMDk5Njc3MiwxLjM0NTI2NTcxIEMzNC4wODE3OTc4LDEuNDE1NDIxMzIgMzQuMDc2MTA1OSwxLjUwODExMDEyIDM0LjA3NjEwNTksMS41OTI3OTQ2IEwzNC4wNzYxMDU5LDMuOTg2ODk2NzIgQzM0LjA3NjEwNTksNC4wNTQ2MzA4NSAzNC4wODAxOTA3LDQuMTI3NjExNTEgMzQuMDg5NzY2NSw0LjE4NjEzMDU3IEMzNC4xMTI1MzQyLDQuMzI3NTE4IDM0LjI0Mzg1MDEsNC41NjgyNTMzIDM0LjYyMDk4OTksNC41NjgyNTMzIEMzNS4wMDA0MDY0LDQuNTY4MjUzMyAzNS4xMzQxMzMsNC4zMjc1MTggMzUuMTU1MzYwNSw0LjE4NjEzMDU3IEMzNS4xNjY5NDUyLDQuMTI3NjExNTEgMzUuMTcwNjI4Miw0LjA1NDYzMDg1IDM1LjE2ODk1NDEsMy45ODY4OTY3MiBMMzUuMTY4OTU0MSwzLjIyODkwNjc2IEwzNC42MzQ0NDk2LDMuMjI4OTA2NzYgTDM0LjYzNDQ0OTYsMi40NjQ5MzAzNiBMMzYuNDc5MTY2NywyLjQ2NDkzMDM2IEwzNi40NzkxNjY3LDMuODY4NTEzMzQgQzM2LjQ3NzA5MDgsMy45NjQ0MzA3OCAzNi40NzU0ODM3LDQuMDM4Njg5NDUgMzYuNDYwMDE1LDQuMjEzOTc3NTcgQzM2LjM3MjgyODIsNS4xNjk1ODcwNyAzNS41NTM5OTU2LDUuNTA4MzI0OTcgMzQuNjI2MDc5MSw1LjUwODMyNDk3IEMzMy43MDM4NTQ1LDUuNTA4MzI0OTcgMzIuODc5MzMsNS4xNjk1ODcwNyAzMi43OTM2MTY0LDQuMjEzOTc3NTcgQzMyLjc3NTkzOCw0LjAzODY4OTQ1IDMyLjc3Mjg1NzYsMy45NjQ0MzA3OCAzMi43NzI4NTc2LDMuODY4NTEzMzQgTDMyLjc3Mjg1NzYsMS42NjYxNzg4NCBDMzIuNzcyODU3NiwxLjU3MDI2MTQgMzIuNzg4NzI4LDEuNDA5MDk4NTcgMzIuNzk5MTA3NCwxLjMxOTUwMzg3IEMzMi45MTQxNTExLDAuMzM5NzQ2ODU1IDMzLjcwMzg1NDUsMC4wMjU5NjM2MjgyIDM0LjYyOTc2MjEsMC4wMjU5NjM2MjgyIFogTTEyLjE0NDc0NDcsMC4xNjY4ODAyMTEgTDEyLjgwMjI2MTYsNC4yNjE1OTk5OCBMMTMuNDYwMTgwNCwwLjE2Njg4MDIxMSBMMTUuNTg1Mjc0NiwwLjE2Njg4MDIxMSBMMTUuNzAxOTI1NSw1LjQwNTIxMDM2IEwxNC4zOTUyNjIsNS40MDUyMTAzNiBMMTQuMzU5ODM4MiwwLjU1NTkzMTA1NCBMMTMuNDYyMjU2Miw1LjQwNTIxMDM2IEwxMi4xMzk4NTYzLDUuNDA1MjEwMzYgTDExLjI0MzA3NzksMC41NTU5MzEwNTQgTDExLjIwNzc4OCw1LjQwNTIxMDM2IEw5LjkwNDQwNTgsNS40MDUyMTAzNiBMMTAuMDE3MjM5NywwLjE2Njg4MDIxMSBMMTIuMTQ0NzQ0NywwLjE2Njg4MDIxMSBaIE03LjkwMTI1MjUsMC4xNjY4ODAyMTEgTDguODYzMjUzNTgsNS40MDUyMTAzNiBMNy40NjQzMTQxLDUuNDA1MjEwMzYgTDYuNzUzODI4ODMsMC41NTU5MzEwNTQgTDYuMDI1ODY2MDIsNS40MDUyMTAzNiBMNC42MTcxNDk4Myw1LjQwNTIxMDM2IEw1LjU4MzE2ODczLDAuMTY2ODgwMjExIEw3LjkwMTI1MjUsMC4xNjY4ODAyMTEgWiBNMjguMzA0ODM2LDUuMzUwNTkyNTcgTDI3LjAyNDYyMzMsNS4zNTA1OTI1NyBMMjcuMDI0NjIzMywwLjE2Njg4MDIxMSBMMjguOTU5ODc1MywwLjE2Njg4MDIxMSBMMzAuMTg3OTkwMyw0LjM4NDM1NTQ3IEwzMC4xMTY4NzQ4LDAuMTY2ODgwMjExIEwzMS40MDU0NTgxLDAuMTY2ODgwMjExIEwzMS40MDU0NTgxLDUuMzUwNTkyNTcgTDI5LjU0OTQyNDEsNS4zNTA1OTI1NyBMMjguMjMsMC45OTkgTDI4LjMwNDgzNiw1LjM1MDU5MjU3IFoiPjwvcGF0aD48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iaTUiPjxwYXRoIGQ9Ik0yNC4zMzUyOTY2LDIuNDcwMDY0MzIgQzI1LjMwOTY1MDIsMi40NzAwNjQzMiAyNi4xMjEzMjMsMi42NTY2MDU2NCAyNi43NjkyNzY4LDMuMDI4OTczNTYgQzI3LjQxNzIzMDUsMy40MDE2OTg4MyAyNy45Mjk1MDE3LDMuOTA4NDMzNjcgMjguMzA2MDkwMiw0LjU1MDI1MDE1IEwyNi4zOTU0NTczLDUuNDgyNTk5MzcgQzI2LjE5NjA4NjksNS4xNDYzMjQ3IDI1LjkxOTE4MzYsNC44ODAwOTIzNiAyNS41NjQ3NDczLDQuNjg0NjE3MDggQzI1LjIxMDMxMTEsNC40ODkxNDE3OSAyNC44MDA0OTQyLDQuMzkxMjI1NDcgMjQuMzM1Mjk2Niw0LjM5MTIyNTQ3IEMyMy44MDM2NDIyLDQuMzkxMjI1NDcgMjMuNDEwNDM5NSw0LjQ5NDg1OTUzIDIzLjE1NTY4ODUsNC43MDIxMjc2NiBDMjIuOTAwNTkxMyw0LjkwOTM5NTc5IDIyLjc3MzU2MTksNS4xNDk1NDA5MyAyMi43NzM1NjE5LDUuNDIyMjA1NzMgQzIyLjc3MzU2MTksNS43Mzg0NjgzMSAyMi45NjE4NTYyLDUuOTY1MDMzODEgMjMuMzM4NDQ0Nyw2LjEwMDgzMDE3IEMyMy43MTQ2ODcsNi4yMzczNDEyNSAyNC4yNjg4Mzk4LDYuMzgyMDcxNTggMjQuOTk5ODY0Niw2LjUzNDY2MzgxIEMyNS4zOTg2MDU0LDYuNjExMTM4NiAyNS43OTk3NjksNi43MTE1NTY0NCAyNi4yMDQzOTQsNi44MzczNDY3NSBDMjYuNjA4NjcyOSw2Ljk2Mjc3OTcgMjYuOTc2OTU0Myw3LjEzMTgxMDQzIDI3LjMwOTIzODMsNy4zNDQ3OTYzMSBDMjcuNjQxNTIyMiw3LjU1NzQyNDgyIDI3LjkxMDExODUsNy44Mjk3MzIyNiAyOC4xMTUwMjY5LDguMTYyNzkwNyBDMjguMzE5OTM1NCw4LjQ5NTQ5MTc4IDI4LjQyMjM4OTYsOC45MTI1Mjk1NSAyOC40MjIzODk2LDkuNDE0MjYxMzcgQzI4LjQyMjM4OTYsOS43NTIzMjI4MyAyOC4zNDQ4NTY3LDEwLjEwNDMyMTMgMjguMTg5NzkwOCwxMC40Njk1NDIgQzI4LjAzNDM3ODgsMTAuODM0NzYyOCAyNy43OTM4MTkxLDExLjE3MzE4MTYgMjcuNDY3MDczMSwxMS40ODM3MjY0IEMyNy4xNDAzMjcyLDExLjc5NDk4NiAyNi43Mjc3NDEzLDEyLjA0ODM1MzQgMjYuMjI5MzE1MywxMi4yNDQ1NDM0IEMyNS43MzA4ODkzLDEyLjQ0MTA5MDggMjUuMTMyNzc4MiwxMi41MzkwMDcxIDI0LjQzNDk4MTgsMTIuNTM5MDA3MSBDMjMuMzYwNTk2OSwxMi41MzkwMDcxIDIyLjQ2ODk2ODIsMTIuMzMyODExIDIxLjc2MDA5NTcsMTEuOTIwMDYxNiBDMjEuMDUxMjIzMywxMS41MDY5NTQ4IDIwLjUwMjk1NDcsMTAuOTEwNTIyOCAyMC4xMTUyOSwxMC4xMzAwNTExIEwyMi4xOTIwNjQ5LDkuMTY1MTgyMjUgQzIyLjQyNDY2MzcsOS42MDQ3MzM2MyAyMi43NDAzMzM1LDkuOTQyNDM3NzQgMjMuMTM5MDc0MywxMC4xNzg2NTE5IEMyMy41Mzc4MTUxLDEwLjQxNDUwODggMjQuMDAzMDEyNiwxMC41MzIwNzk4IDI0LjUzNDY2NywxMC41MzIwNzk4IEMyNS4wODg0NzM2LDEwLjUzMjA3OTggMjUuNTAzODI4NiwxMC40MTc3MjUgMjUuNzgwNzMxOSwxMC4xODkwMTUzIEMyNi4wNTcyODkxLDkuOTU5OTQ4MzIgMjYuMTk2MDg2OSw5LjY4NzY0MDg4IDI2LjE5NjA4NjksOS4zNzEwMjA5NSBDMjYuMTk2MDg2OSw5LjE5NjYyOTgzIDI2LjEzMjM5OTIsOS4wNTUxMTU3MyAyNi4wMDUwMjM2LDguOTQ1NzYzOTIgQzI1Ljg3NzY0ODEsOC44MzcxMjY4MyAyNS43MTE1MDYxLDguNzQxMzU0NjYgMjUuNTA2NTk3Niw4LjY1OTg3Njg1IEMyNS4zMDEzNDMxLDguNTc4MDQxNjcgMjUuMDYwNzgzMyw4LjUxMDE0MzQ5IDI0Ljc4Mzg4LDguNDU1MTEwMjMgQzI0LjUwNjk3NjcsOC40MDA3OTE2OSAyNC4yMTg5OTcyLDguMzQwNzU1NCAyMy45MTk5NDE2LDguMjc1MzU4NzMgQzIzLjQ5ODcwMjUsOC4xODgxNjMxOCAyMy4wODY0NjI2LDguMDg0ODg2NDcgMjIuNjgyMTgzOCw3Ljk2NDgxMzkgQzIyLjI3NzkwNSw3Ljg0NTA5ODY5IDIxLjkxNTE2MTYsNy42Nzg1Njk0NyAyMS41OTM5NTM4LDcuNDY2Mjk4MzEgQzIxLjI3Mjc0NTksNy4yNTMzMTI0NCAyMS4wMTI0NTY4LDYuOTgwNjQ3NjQgMjAuODEzMDg2NCw2LjY0ODMwMzkyIEMyMC42MTM3MTYsNi4zMTU5NjAyIDIwLjUxNDAzMDgsNS44OTg5MjI0MyAyMC41MTQwMzA4LDUuMzk3NTQ3OTcgQzIwLjUxNDAzMDgsNS4wMTU1MzEzNyAyMC42MDU0MDg5LDQuNjQ3ODA5MTIgMjAuNzg4MTY1MSw0LjI5MzY2NjUgQzIwLjk3MDkyMTMsMy45MzkxNjY1MyAyMS4yMjg0NDE0LDMuNjI2MTIwMTggMjEuNTYwNzI1NCwzLjM1MzA5ODAzIEMyMS44OTMwMDkzLDMuMDgwNzkwNTkgMjIuMjk0MTczLDIuODY1NjYwNTYgMjIuNzY1MjU0OCwyLjcwNzM1MDYgQzIzLjIzNTk5MDQsMi41NDkwNDA2MyAyMy43NTkzMzc3LDIuNDcwMDY0MzIgMjQuMzM1Mjk2NiwyLjQ3MDA2NDMyIFogTTMzLjEwNzM1MTUsMi40NzAwNjQzMiBDMzQuMDgxNzA1LDIuNDcwMDY0MzIgMzQuODkzMzc3OSwyLjY1NjYwNTY0IDM1LjU0MTMzMTYsMy4wMjg5NzM1NiBDMzYuMTg5Mjg1NCwzLjQwMTY5ODgzIDM2LjcwMTU1NjUsMy45MDg0MzM2NyAzNy4wNzgxNDUxLDQuNTUwMjUwMTUgTDM1LjE2NzUxMjIsNS40ODI1OTkzNyBDMzQuOTY4MTQxOCw1LjE0NjMyNDcgMzQuNjkxMjM4NCw0Ljg4MDA5MjM2IDM0LjMzNjgwMjIsNC42ODQ2MTcwOCBDMzMuOTgyMzY1OSw0LjQ4OTE0MTc5IDMzLjU3MjU0OSw0LjM5MTIyNTQ3IDMzLjEwNzM1MTUsNC4zOTEyMjU0NyBDMzIuNTc1Njk3MSw0LjM5MTIyNTQ3IDMyLjE4MjQ5NDQsNC40OTQ4NTk1MyAzMS45Mjc3NDMzLDQuNzAyMTI3NjYgQzMxLjY3MjY0NjEsNC45MDkzOTU3OSAzMS41NDU2MTY3LDUuMTQ5NTQwOTMgMzEuNTQ1NjE2Nyw1LjQyMjIwNTczIEMzMS41NDU2MTY3LDUuNzM4NDY4MzEgMzEuNzMzOTExLDUuOTY1MDMzODEgMzIuMTEwNDk5NSw2LjEwMDgzMDE3IEMzMi40ODY3NDE5LDYuMjM3MzQxMjUgMzMuMDQwODk0Nyw2LjM4MjA3MTU4IDMzLjc3MTkxOTQsNi41MzQ2NjM4MSBDMzQuMTcwNjYwMiw2LjYxMTEzODYgMzQuNTcxODIzOSw2LjcxMTU1NjQ0IDM0Ljk3NjQ0ODksNi44MzczNDY3NSBDMzUuMzgwNzI3Nyw2Ljk2Mjc3OTcgMzUuNzQ5MDA5MSw3LjEzMTgxMDQzIDM2LjA4MTI5MzEsNy4zNDQ3OTYzMSBDMzYuNDEzNTc3MSw3LjU1NzQyNDgyIDM2LjY4MjE3MzMsNy44Mjk3MzIyNiAzNi44ODcwODE4LDguMTYyNzkwNyBDMzcuMDkxOTkwMiw4LjQ5NTQ5MTc4IDM3LjE5NDQ0NDQsOC45MTI1Mjk1NSAzNy4xOTQ0NDQ0LDkuNDE0MjYxMzcgQzM3LjE5NDQ0NDQsOS43NTIzMjI4MyAzNy4xMTY5MTE1LDEwLjEwNDMyMTMgMzYuOTYxODQ1NywxMC40Njk1NDIgQzM2LjgwNjQzMzcsMTAuODM0NzYyOCAzNi41NjU4NzM5LDExLjE3MzE4MTYgMzYuMjM5MTI4LDExLjQ4MzcyNjQgQzM1LjkxMjM4MjEsMTEuNzk0OTg2IDM1LjQ5OTc5NjEsMTIuMDQ4MzUzNCAzNS4wMDEzNzAyLDEyLjI0NDU0MzQgQzM0LjUwMjk0NDIsMTIuNDQxMDkwOCAzMy45MDQ4MzMsMTIuNTM5MDA3MSAzMy4yMDcwMzY3LDEyLjUzOTAwNzEgQzMyLjEzMjY1MTgsMTIuNTM5MDA3MSAzMS4yNDEwMjMxLDEyLjMzMjgxMSAzMC41MzIxNTA2LDExLjkyMDA2MTYgQzI5LjgyMzI3ODEsMTEuNTA2OTU0OCAyOS4yNzUwMDk1LDEwLjkxMDUyMjggMjguODg3MzQ0OSwxMC4xMzAwNTExIEwzMC45NjQxMTk4LDkuMTY1MTgyMjUgQzMxLjE5NjcxODYsOS42MDQ3MzM2MyAzMS41MTIzODgzLDkuOTQyNDM3NzQgMzEuOTExMTI5MSwxMC4xNzg2NTE5IEMzMi4zMDk4Njk5LDEwLjQxNDUwODggMzIuNzc1MDY3NSwxMC41MzIwNzk4IDMzLjMwNjcyMTgsMTAuNTMyMDc5OCBDMzMuODYwNTI4NSwxMC41MzIwNzk4IDM0LjI3NTg4MzUsMTAuNDE3NzI1IDM0LjU1Mjc4NjgsMTAuMTg5MDE1MyBDMzQuODI5MzQ0LDkuOTU5OTQ4MzIgMzQuOTY4MTQxOCw5LjY4NzY0MDg4IDM0Ljk2ODE0MTgsOS4zNzEwMjA5NSBDMzQuOTY4MTQxOCw5LjE5NjYyOTgzIDM0LjkwNDQ1NCw5LjA1NTExNTczIDM0Ljc3NzA3ODUsOC45NDU3NjM5MiBDMzQuNjQ5NzAyOSw4LjgzNzEyNjgzIDM0LjQ4MzU2MSw4Ljc0MTM1NDY2IDM0LjI3ODY1MjUsOC42NTk4NzY4NSBDMzQuMDczMzk3OSw4LjU3ODA0MTY3IDMzLjgzMjgzODIsOC41MTAxNDM0OSAzMy41NTU5MzQ4LDguNDU1MTEwMjMgQzMzLjI3OTAzMTUsOC40MDA3OTE2OSAzMi45OTEwNTIxLDguMzQwNzU1NCAzMi42OTE5OTY1LDguMjc1MzU4NzMgQzMyLjI3MDc1NzMsOC4xODgxNjMxOCAzMS44NTg1MTc1LDguMDg0ODg2NDcgMzEuNDU0MjM4Niw3Ljk2NDgxMzkgQzMxLjA0OTk1OTgsNy44NDUwOTg2OSAzMC42ODcyMTY1LDcuNjc4NTY5NDcgMzAuMzY2MDA4Niw3LjQ2NjI5ODMxIEMzMC4wNDQ4MDA4LDcuMjUzMzEyNDQgMjkuNzg0NTExNiw2Ljk4MDY0NzY0IDI5LjU4NTE0MTIsNi42NDgzMDM5MiBDMjkuMzg1NzcwOSw2LjMxNTk2MDIgMjkuMjg2MDg1Nyw1Ljg5ODkyMjQzIDI5LjI4NjA4NTcsNS4zOTc1NDc5NyBDMjkuMjg2MDg1Nyw1LjAxNTUzMTM3IDI5LjM3NzQ2MzgsNC42NDc4MDkxMiAyOS41NjAyMTk5LDQuMjkzNjY2NSBDMjkuNzQyOTc2MSwzLjkzOTE2NjUzIDMwLjAwMDQ5NjIsMy42MjYxMjAxOCAzMC4zMzI3ODAyLDMuMzUzMDk4MDMgQzMwLjY2NTA2NDIsMy4wODA3OTA1OSAzMS4wNjYyMjc5LDIuODY1NjYwNTYgMzEuNTM3MzA5NiwyLjcwNzM1MDYgQzMyLjAwODA0NTMsMi41NDkwNDA2MyAzMi41MzEzOTI2LDIuNDcwMDY0MzIgMzMuMTA3MzUxNSwyLjQ3MDA2NDMyIFogTTEzLjc3MzIwNTcsMi40NzAwMjg1OSBDMTQuNDE1NjIxNCwyLjQ3MDAyODU5IDE1LjAwODE5NDUsMi41OTAxMDExNiAxNS41NTA5MjUsMi44Mjk1MzE1OSBDMTYuMDkzMzA5NCwzLjA2OTY3NjczIDE2LjU0MjIzODksMy4zOTY2NjAwNyAxNi44OTY2NzUxLDMuODEwODM4OTcgTDE2Ljg5NjY3NTEsMi40ODcxODE4MSBMMTkuMTM5NTkyLDIuNDg3MTgxODEgTDE5LjEzOTU5MiwxMi41MjE4MTgxIEwxNi44OTY2NzUxLDEyLjUyMTgxODEgTDE2Ljg5NjY3NTEsMTEuMDk1OTU2MyBDMTYuNTQyMjM4OSwxMS41NDQwODQzIDE2LjA4ODExNzQsMTEuODk2Nzk3NSAxNS41MzQzMTA4LDEyLjE1MzczODUgQzE0Ljk4MDUwNDIsMTIuNDEwNjc5NSAxNC4zODIzOTMsMTIuNTM4OTcxNCAxMy43Mzk5NzczLDEyLjUzODk3MTQgQzEzLjE1Mjk0MjMsMTIuNTM4OTcxNCAxMi41NzQyMTQzLDEyLjQyNjQwMzMgMTIuMDAzNzkzNSwxMi4yMDA1NTI1IEMxMS40MzMzNzI2LDExLjk3NDM0NDQgMTAuOTIxMTAxNSwxMS42NDYyODkgMTAuNDY2OTgwMSwxMS4yMTYzODYzIEMxMC4wMTI4NTg2LDEwLjc4NjQ4MzYgOS42NDcwMDAxMSwxMC4yNjAwOTQgOS4zNzA0NDI5Miw5LjYzNzU3NDkxIEM5LjA5MzUzOTYsOS4wMTQ2OTg0NCA4Ljk1NTA4Nzk0LDguMzA2NDEzMjIgOC45NTUwODc5NCw3LjUxMzA3NjU4IEM4Ljk1NTA4Nzk0LDYuNzA4MzA0NDcgOS4wOTA0MjQ0NCw1Ljk5NTAxNjIyIDkuMzYyMTM1ODIsNS4zNzE3ODI0IEM5LjYzMzUwMTA3LDQuNzQ4OTA1OTMgOS45OTM0NzUzOSw0LjIyMjg3MzcxIDEwLjQ0MjA1ODgsMy43OTI5NzEwMyBDMTAuODkwNjQyMSwzLjM2MjcxMDk4IDExLjQwNTY4MjMsMy4wMzUwMTI5MiAxMS45ODcxNzkzLDIuODA5NTE5NDkgQzEyLjU2ODY3NjMsMi41ODMzMTEzNCAxMy4xNjQwMTg0LDIuNDcwMDI4NTkgMTMuNzczMjA1NywyLjQ3MDAyODU5IFogTTQuMTUzNTQ5NzgsMCBDNC43ODQ4ODkzNSwwIDUuMzY2Mzg2MzIsMC4xMTcyMTM3MDEgNS44OTgwNDA2OSwwLjM1MTk5ODQ2MSBDNi40Mjk2OTUwNiwwLjU4NjQyNTg2MiA2Ljg4OTAwODQ0LDAuOTAzNDAzMTU2IDcuMjc3MDE5MjIsMS4zMDQwMDI0MiBDNy42NjQ2ODM4NiwxLjcwNDI0NDMyIDcuOTY4OTMxMzgsMi4xNzU5NTggOC4xOTA4MDAxNywyLjcxOTE0MzQ0IEM4LjQxMjMyMjgyLDMuMjYxOTcxNTIgOC41MjMwODQxNSwzLjg0MjMyMjI4IDguNTIzMDg0MTUsNC40NjAxOTU3MiBDOC41MjMwODQxNSw1LjA3NzM1NDQ0IDguNDEyMzIyODIsNS42NjA1NjQwOCA4LjE5MDgwMDE3LDYuMjA5NDY3MjYgQzcuOTY4OTMxMzgsNi43NTgzNzA0NCA3LjY2NDY4Mzg2LDcuMjMyOTQyOTkgNy4yNzcwMTkyMiw3LjYzMzU0MjI1IEM2Ljg4OTAwODQ0LDguMDMzNzg0MTYgNi40MjY5MjYwMyw4LjM1MTExODgxIDUuODg5NzMzNTksOC41ODUxODg4NSBDNS4zNTIxOTUwMiw4LjgxOTYxNjI1IDQuNzY4Mjc1MTUsOC45MzY4Mjk5NSA0LjEzNjkzNTU4LDguOTM2ODI5OTUgTDIuMjU5NTMxMDgsOC45MzY4Mjk5NSBMMi4yNTk1MzEwOCwxMi41MjE4NTM5IEwwLDEyLjUyMTg1MzkgTDAsMCBMNC4xNTM1NDk3OCwwIFogTTE0LjEwNTQ4OTcsNC41ODAyMzI1NiBDMTMuNjk1NjcyOCw0LjU4MDIzMjU2IDEzLjMxMDc3NzEsNC42NTU2MzUyNyAxMi45NTA4MDI4LDQuODA1NzI1OTkgQzEyLjU5MDgyODUsNC45NTU4MTY3IDEyLjI3NzkyNzgsNS4xNjAyMjU5NiAxMi4wMTIxMDA2LDUuNDE3NTI0MzMgQzExLjc0NjI3MzQsNS42NzU1Mzc0MSAxMS41Mzg1OTU5LDUuOTgxNzkzOTQgMTEuMzg5MDY4MSw2LjMzNjI5MzkxIEMxMS4yMzk1NDAzLDYuNjkwNzkzODkgMTEuMTY0Nzc2NCw3LjA3MTczODQxIDExLjE2NDc3NjQsNy40ODAxOTk1NyBDMTEuMTY0Nzc2NCw3Ljg4ODMwMzM3IDExLjIzOTU0MDMsOC4yNzI0NjQxMyAxMS4zODkwNjgxLDguNjMxOTY3MTIgQzExLjUzODU5NTksOC45OTE0NzAxMiAxMS43NDYyNzM0LDkuMzAyNzI5NjcgMTIuMDEyMTAwNiw5LjU2NjQ2MDUgQzEyLjI3NzkyNzgsOS44Mjk0NzY2MSAxMi41OTA4Mjg1LDEwLjAzNjAzIDEyLjk1MDgwMjgsMTAuMTg2ODM1NCBDMTMuMzEwNzc3MSwxMC4zMzY5MjYyIDEzLjY5NTY3MjgsMTAuNDExOTcxNSAxNC4xMDU0ODk3LDEwLjQxMTk3MTUgQzE0LjUyNjM4MjcsMTAuNDExOTcxNSAxNC45MTM3MDEyLDEwLjMzNjkyNjIgMTUuMjY4NDgzNiwxMC4xODY4MzU0IEMxNS42MjI5MTk5LDEwLjAzNjAzIDE1LjkyNzE2NzQsOS44MjY2MTc3NCAxNi4xODIyNjQ2LDkuNTU3ODgzODkgQzE2LjQzNzAxNTYsOS4yODk1MDczOSAxNi42MzkxNTUsOC45Nzc4OTA0OCAxNi43ODg2ODI4LDguNjIzNzQ3ODcgQzE2LjkzODIxMDYsOC4yNjk2MDUyNiAxNy4wMTI5NzQ1LDcuODg4MzAzMzcgMTcuMDEyOTc0NSw3LjQ4MDE5OTU3IEMxNy4wMTI5NzQ1LDcuMDgyODE2NTQgMTYuOTM4MjEwNiw2LjcwNjg3NTAzIDE2Ljc4ODY4MjgsNi4zNTIzNzUwNiBDMTYuNjM5MTU1LDUuOTk3ODc1MDkgMTYuNDM3MDE1Niw1LjY4OTExNzA1IDE2LjE4MjI2NDYsNS40MjYxMDA5NCBDMTUuOTI3MTY3NCw1LjE2MjcyNzQ3IDE1LjYyMjkxOTksNC45NTU4MTY3IDE1LjI2ODQ4MzYsNC44MDU3MjU5OSBDMTQuOTEzNzAxMiw0LjY1NTYzNTI3IDE0LjUyNjM4MjcsNC41ODAyMzI1NiAxNC4xMDU0ODk3LDQuNTgwMjMyNTYgWiBNMy45ODc0MDc3OSwyLjE2MTMwNjI4IEwyLjI1OTUzMTA4LDIuMTYxMzA2MjggTDIuMjU5NTMxMDgsNi43NzU1MjM2NyBMMy45ODc0MDc3OSw2Ljc3NTUyMzY3IEM0LjMxOTY5MTc3LDYuNzc1NTIzNjcgNC42MjQyODU0Miw2LjcxNTQ4NzM4IDQuOTAxMTg4NzQsNi41OTU0MTQ4MSBDNS4xNzc3NDU5Myw2LjQ3NTM0MjI0IDUuNDE2MjI4OTIsNi4zMDk1Mjc3NCA1LjYxNTU5OTMsNi4wOTgzMjg2NiBDNS44MTQ5Njk2OSw1Ljg4Njc3MjIyIDUuOTcwMDM1NTUsNS42NDA1NTE5OCA2LjA4MDc5Njg4LDUuMzYwMzgyNjUgQzYuMTkxMjEyMDgsNS4wODA1NzA2NyA2LjI0NjkzODg3LDQuNzgwMDMxODkgNi4yNDY5Mzg4Nyw0LjQ2MDE5NTcyIEM2LjI0NjkzODg3LDQuMTM5NjQ0ODQgNi4xOTEyMTIwOCwzLjgzOTQ2MzQxIDYuMDgwNzk2ODgsMy41NTkyOTQwOCBDNS45NzAwMzU1NSwzLjI3OTEyNDc1IDUuODE0OTY5NjksMy4wMzYxMjA3MyA1LjYxNTU5OTMsMi44MzAyODIwNCBDNS40MTYyMjg5MiwyLjYyNDQ0MzM0IDUuMTc3NzQ1OTMsMi40NjE0ODc3MSA0LjkwMTE4ODc0LDIuMzQxNDE1MTQgQzQuNjI0Mjg1NDIsMi4yMjEzNDI1NyA0LjMxOTY5MTc3LDIuMTYxMzA2MjggMy45ODc0MDc3OSwyLjE2MTMwNjI4IFoiPjwvcGF0aD48L2NsaXBQYXRoPjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMzLjAgLTE2MC4wKSI+PGcgY2xpcC1wYXRoPSJ1cmwoI2kwKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjMzLjAgMTYwLjApIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC4wMDAxODA1NTU1NTU1NTIwNzU0OCAwLjApIj48ZyBjbGlwLXBhdGg9InVybCgjaTEpIj48cG9seWdvbiBwb2ludHM9IjAsMCA1MiwwIDUyLDUyIDAsNTIgMCwwIiBzdHJva2U9Im5vbmUiIGZpbGw9InVybCgjaTIpIj48L3BvbHlnb24+PC9nPjwvZz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3LjU4MzMzMzMzMzMzMzMzMiAxNC44MDU1NTU1NTU1NTU1NSkiPjxnIGNsaXAtcGF0aD0idXJsKCNpMykiPjxnIGNsaXAtcGF0aD0idXJsKCNpNCkiPjxwb2x5Z29uIHBvaW50cz0iMCwwIDM2LjQ3OTE2NjcsMCAzNi40NzkxNjY3LDUuNjEyMTc5NDkgMCw1LjYxMjE3OTQ5IDAsMCIgc3Ryb2tlPSJub25lIiBmaWxsPSIjRkZGRkZGIj48L3BvbHlnb24+PC9nPjwvZz48L2c+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNy41ODMzMzMzMzMzMzMzNzEgMjQuNDMyMDgwMjU1NDc3MzMpIj48ZyBjbGlwLXBhdGg9InVybCgjaTUpIj48cG9seWdvbiBwb2ludHM9IjAsMCAzNy4xOTQ0NDQ0LDAgMzcuMTk0NDQ0NCwxMi41MzkwMDcxIDAsMTIuNTM5MDA3MSAwLDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0iI0ZGRkZGRiI+PC9wb2x5Z29uPjwvZz48L2c+PC9nPjwvZz48L2c+PC9zdmc+\"\n  },\n  \"e416201b-afeb-41ca-a03d-2281c28322aa\": {\n    \"name\": \"ATKey.Pro CTAP2.1\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"cfcb13a2-244f-4b36-9077-82b79d6a7de7\": {\n    \"name\": \"USB/NFC Passcode Authenticator\",\n    \"icon_light\": null,\n    \"icon_dark\": null\n  },\n  \"91ad6b93-264b-4987-8737-3a690cad6917\": {\n    \"name\": \"Token Ring FIDO2 Authenticator\",\n    \"icon_light\": null,\n    \"icon_dark\": null\n  },\n  \"5753362b-4e6b-6345-7b2f-255438404c75\": {\n    \"name\": \"WiSECURE Blentity FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"9f77e279-a6e2-4d58-b700-31e5943c6a98\": {\n    \"name\": \"Hyper FIDO Pro\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"0bb43545-fd2c-4185-87dd-feb0b2916ace\": {\n    \"name\": \"Security Key NFC by Yubico - Enterprise Edition\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"73402251-f2a8-4f03-873e-3cb6db604b03\": {\n    \"name\": \"uTrust FIDO2 Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAOCAYAAADZjbloAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAffaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0OCA3OS4xNjQwMzYsIDIwMTkvMDgvMTMtMDE6MDY6NTcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMDQtMTBUMTE6NDY6MTYtMDQ6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUyM2FkMzNkLTkwMjMtNGNlNS05MGJmLWUzZmExZDdjMGFlNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjBhMTFlZTdmLWQ5ZTQtYWM0NC1hM2I2LTllZmVkYTA0NDA5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmI4ZGRmYTA5LTdiM2MtNDMwMy1iNTlmLWE2MTQyZTdiMTJhYSI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjhkZGZhMDktN2IzYy00MzAzLWI1OWYtYTYxNDJlN2IxMmFhIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTEwVDExOjQ2OjE2LTA0OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY29udmVydGVkIiBzdEV2dDpwYXJhbWV0ZXJzPSJmcm9tIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AgdG8gaW1hZ2UvcG5nIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MjNhZDMzZC05MDIzLTRjZTUtOTBiZi1lM2ZhMWQ3YzBhZTYiIHN0RXZ0OndoZW49IjIwMjAtMDQtMTBUMTE6NDY6MzItMDQ6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4xIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8eG1wTU06SW5ncmVkaWVudHM+IDxyZGY6QmFnPiA8cmRmOmxpIHN0UmVmOmxpbmtGb3JtPSJSZWZlcmVuY2VTdHJlYW0iIHN0UmVmOmZp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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAOCAYAAADZjbloAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAffaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0OCA3OS4xNjQwMzYsIDIwMTkvMDgvMTMtMDE6MDY6NTcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMDQtMTBUMTE6NDY6MTYtMDQ6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUyM2FkMzNkLTkwMjMtNGNlNS05MGJmLWUzZmExZDdjMGFlNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjBhMTFlZTdmLWQ5ZTQtYWM0NC1hM2I2LTllZmVkYTA0NDA5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmI4ZGRmYTA5LTdiM2MtNDMwMy1iNTlmLWE2MTQyZTdiMTJhYSI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjhkZGZhMDktN2IzYy00MzAzLWI1OWYtYTYxNDJlN2IxMmFhIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTEwVDExOjQ2OjE2LTA0OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY29udmVydGVkIiBzdEV2dDpwYXJhbWV0ZXJzPSJmcm9tIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AgdG8gaW1hZ2UvcG5nIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MjNhZDMzZC05MDIzLTRjZTUtOTBiZi1lM2ZhMWQ3YzBhZTYiIHN0RXZ0OndoZW49IjIwMjAtMDQtMTBUMTE6NDY6MzItMDQ6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4xIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8eG1wTU06SW5ncmVkaWVudHM+IDxyZGY6QmFnPiA8cmRmOmxpIHN0UmVmOmxpbmtGb3JtPSJSZWZlcmVuY2VTdHJlYW0iIHN0UmVmOmZp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\"\n  },\n  \"c1f9a0bc-1dd2-404a-b27f-8e29047a43fd\": {\n    \"name\": \"YubiKey 5 FIPS Series with NFC\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"504d7149-4e4c-3841-4555-55445a677357\": {\n    \"name\": \"WiSECURE AuthTron USB FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"a3975549-b191-fd67-b8fb-017e2917fdb3\": {\n    \"name\": \"Excelsecu eSecu FIDO2 NFC Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"19083c3d-8383-4b18-bc03-8f1c9ab2fd1b\": {\n    \"name\": \"YubiKey 5 Series\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"da1fa263-8b25-42b6-a820-c0036f21ba7f\": {\n    \"name\": \"ATKey.Card NFC\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGoAAAApCAIAAABSjysCAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAnuSURBVGhD7Zp3VFRXHsd/9DpDG4be0TWaY8tuLMfGIWY1GBNNNKtRY1nNBiRR0E10dSMi4ipSrCsnmxVDF6UZwRJ7AYOKrsbFRLDSg40+wOz3vfuExzAYTOCPxfme75lz7+/+3hv4cN/93TsDfe8zJ0va/5BsUI86WzYoS9v1dnScsneJcsdMSyWHdHJKIcses1U6uWWQVWHkbuFte4sod9z0VLI76j6qIGjzD39d1wMOubEqNI3sM0hWGBUjvG1vEYdvH8ly35svBHpG+8isN+M77/OhEOgZvSz49pJREhkmk8lvdzxR/rzP2W1fFnwZ+i4Z5JpB7s9xJnlkkideVeLt7bGfZNc/C2a3fbnw8YDUG1wSSTeBtFPIjIfIaKqxBp+qMa1SyLT65u2nNwoL1kYmk0QlQWwNPlUjM39RAMuE9pNlBrmp5LRag0/VCaTF0loUtXi9Fb5rL5mq5LRag6+d95M8b9ZfkKOofrRDJmX5KSTtbAJq8LVzAlFzYwNyji6Y8zeix4UFaBcER/A1RDUZ1uBrcyrZ5k6Zw3KCicKJkka8xrrJZICSopIPa/C1GXtgxcOHSDjh//Emoq06OkFE9RWliPywcn0Kmavkwxp8glPJ/qz3VJawmmgj7xCi2KGvsiB2gh0noAafYKx6dcUlGC0+c+rksmU5a9bkfPklfNzPF0UY8St+K/aRlcpVGnyc08jx5IgJbPQ5SiI9lQmowccZh/+nNwqaG+oUTx/DDVVVLLOpvk7x6BELopu/cGkq2Ykv1OCDPbExjiH6BqWW9OF/EzVUlCHz0gefxApBg91EcUSozqILNfi4VU+3Kuci4ne/TkwhC9AErMZH3AS86rcileTIwb5PqWxGJHfyLDHBzvCVFBdvWBsSuja4urqGjYoVvX1HVFgEEuJjvkF3Q3DIxnXrbxcV/ffGjU3rQ7eER4odtTkiclMY0nZEbYnYGLYlPIK/hxptDY9AwrbIKKHfTXoevnRyznYZyuJgwU4Xbfh8V+AcggiKxrUlqxFpqq1FkWHXwp3hy805b0RaekTgyEZbNX/WHDM9I3N9o76OLiyiQ2RMOkeyshPj49GwlVqILTc1szIyRVpkWLgJ6RoQ5efnswvFupx30ZDIhHSAWwh1k56HL4F0qs59j+C9mORkMmLBjvhQMRJJh79aefaNqdjlsMzO8F26eNHaWGphYFJezi0CrfJdsEhmLHGykr8+cIgQUipBR25qfuLY8b0JCZYGJi7WtmIj2cHcimXaSiycZDaT31RT5f441stZZmNnZin0u0+d4ksnl4PyV1gwjWxbD7Yd8cF4rq+vWIdgQ0V56wR8IXwBi/1ByllmO6TfACHEi8MnMf8284DQ71x4zO3MrSTa+uXl5UKI1907d6TaBmAXFvoPIdR96hRfIumVZX2HSPHejCQyZERgtfgwAXF042+gPDXSJ40cEOw6viW+i60MTZ2tbfu7ebJIq7qOD7LQN3a0kvsuWCj0ec2e/idHmRz3F/rdKvX40sn1gMSdRdBAl2GC1eKDUUAKgrhVvLbwNpuAXcQX4OvP2A1w78PnttML4fNb+DHwYekU+kpldXU15qODpfUXAcuEULdKLT43rHSl3x5FFxMQ07CVEYydiuIJd/i96rdSjA8TMIWEv/CxwV7YbP8iPrTXr1lrbmDsbuf4qkdfPlFVDN+RQ4dv/fTTxnWhUZvDxY4M24x6KqQqlZWVlRJtPXtzWciaIBYJ9P/U0dJaqmNQV8t9QNmNulNUhFc1+LDqZZDTo7xLlSfPZdsNQreVEQyyTXVPkHl96d9TyUY8lELSHzdswVBN0S2kPR+fi9xu+ZIA0Onr5IoaytI6iuE7efxEYly8EWnbSC3Etkblbf9UvjPhLRQQG6k561oYGKM7b+Ys1lXRppDQ9ye9M23yu8GruJ3DC4mIuFe1Dy+/lhkDgQo7mP8KzSKFJDhjoK0yCmT8ELB6Pgef3FgKai7Wdh52jph69uZW4kkkFsOHypuSmITa6ungLLaHnRNuIqTyunb1P9j62EjMD2Zm7ty6jS8mesUPHgjDz4RdJH7/gMWfnj5x8rvDRzaGrBcGuqxO8BmAHSuynrzb0XkRA5/19SVcOYbU4nOV2436/bAJXt6uNvYmWrr3791jyWIxfAe7tvYxDR88FCvp6wMHD+43APsVH+/xwoBI+OXzLlwQOiKdPXU6OS6+uZn7HAT68ebNe3fvNjY25pw7zyKnjh0/nHUIDfX4kkg3kbP+bzeOdJfnBrLbdsTnYe/kZmOPbpNCYaZn6Gbr0M9FKFZiMXxdLB1MWQcOWBtL2PTETLx65Yow8EyJsfHDBw0ROiL5/nnRnA9mfhG4XJeogt/97Nq2fcpbb1ubSPGYo4uNwZtjvD5fGvjagIGsQKniq8rNwyntYW43uOpcbu0dYUJ1XPusjCRlZcLGJebr3cBka2aJlZ5FWvUr8EF9nFy5P4+tw4jBwkfiYgX6f7Z4IfelTWfCQdB/4Sdo7N+bYkjaLLgpdMOEcd6sXVRYiOMQGgK+nMmz2UAPqSM+VN7Kiko2Ck30egNPnKmW7uW8S0KI16/Dt2vbDmxWZEaSI9ncg6aioFWr580QpouKglauHjtsRB9Hl7l8tUmIjXvP5202NHGcd2ZaOmtDbQ/vfrI5PfrdJ9du/Hwmp/t9Nvfx5SvY4nTEp3Jow4nN3dbR0VIu9HkxfIcOZtXW1Fw4n3Mp72J75+Vd4I6VKho2iFv++jm7Cf32woU4HQsdkXAKxNkZjeSExBlTpqEBfFMnTuIHlZPGT0iIjWdtqA1fKjnhoKqycnWrDdK5kt0RX7vT1ZGsbKmuAWbN7OkzhBDDZ8ptXGJjYvA740KxMYpdsZD6TBdycrFfQfGN27OHRR5WPYzevpO1mbxGjhrzh+FCR6lsaWmprq5hRKC5M2ZiEUSDx+fDgnG796DEsXbU5gjsotCgs694p5JhOknTyaznnMZ9HUwFa7fiLc+dOauFn5Towf37/A/TJoAzIm4wesc/uX5LC9raRNiFxMXswXIDLmKj5phq6fGXtmn86LEouA4W1kJfqfxX9Fe4j9B5JuxaUCIcLGRWhiau/O5n0Ufz5CZmqNc4RI4fPQ6Rr3ZGjxs2kk/ntOij+fgTAuLOLVv12OxrqPq5trikrqS0R10L33/QVMNt/ZuaFKVQSYmwO2gvjKDq4ZwvdEtKy0pLFApFQ0NDWWlpZUWF2FBr/WG6XVSEM4admWUE/zngL6qurra5mfuwkgnvwhr19fV4xaxsbFSwCBMirFFfV4dXNUvA/7U+fH8ajhl4qIV+D6tX4Xv65AmmHh7bFYE98gFBR/UqfEv9/HH+k+joN9QLz2BPq/fgAzJsVgb/rv/K5cJ/BPe4lMr/Ac/ARn6lRdQaAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"6002f033-3c07-ce3e-d0f7-0ffe5ed42543\": {\n    \"name\": \"Excelsecu eSecu FIDO2 Fingerprint Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"5fdb81b8-53f0-4967-a881-f5ec26fe4d18\": {\n    \"name\": \"VinCSS FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAYAAAA/IkzyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5AUZAwo2k+OnGwAAHe5JREFUeNrtnXl4ZFWd9z+/e2u5SXfTW1KhQYQBG6STAAO44LigogOMr/owzDiKDg6iqKiMIyCDOAoiIL6I4oIoLoCCwqiviOI2MGwqCi10Kr3QrM3WqaQXOp3kVlJ1fu8fp9J0N9lqSW7dqvN5nurkeTp169xb93vPOb8VHA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA5HsyNRD8ARD0a604AizBPwRY03fu+IiIiiigAGRVWRoqKhqgqta4aiHn7NcIJpcka6Mqgx4nleEmEBsABYDLIYWFJ6LQYWAnsALUByt1cCMMAYMLrTzxAYAoZ3+rkVGAC2lH4fRHVIVEdUTQF8DVbnor4sk+IE0ySMrGhjbGiM1IJ0EliMsCfIfsByYH9gP2AZViDzgVYgBXjU9j4ZF9YIVkCDwGYgBzxTej0NPI2yEdF+lOdQCUkkDPkRgrWbI7uOTjANStidAaOCJ/NBXgx0AYcAK4CXAHtiZ41k1GOdAMXOUNuwYnoWeBJ4HHjM/q5Po/QLbDNqRgUIegdmfWBOMA3CaPcSiqYVkdE0IvsAhwN/V/r5EqANu3RqBMaA54A+rJAeAR7FCuopoA/VzaDbAQ2y/TX7YCeYGBN2d6BGEZEWhIOAo4HXA4dhl1f1OHtMh8HufYZLr+3Y5VuIFUoBKGLFnyq9koBfehns7LQZ+CtwN/A7IB/09FU9uEZ54jQVYXcGUB/VA8STY4FjgSOxs0icHoIhdu+yAVgPPAw8gd3DbAGeQ3UEYQyVMUGLCAbFlM7T13GhCAmQJBBg92FLgUWAj+LXasBxurhNTbiiDXwfVOcjchRwIvD3wD7YjXkcGMNu6h8E/gzcDzyEaj/okCBGJUfQE/UwJ8cJps4JO9uRRBKKxSUqvAl4L/BqrCUrDoxhN+p3AL9H9X6Up1IpyQ+NKAvW1q8JeSKcYOqU/CFtqPEBFiK8DfgAdtmVjnpsM2QAuAP0J8DdGJ4RKKZ74yWQ3XGCqUPsHoUA5Bjgo8DriIdQFDub/Az0xyirxCNfKAjzVle/4a4H3Ka/jsh3ZcAURVW6ED4BnID1vNc7Brtp/wHwYzHFR1TE1NKcWy+4GaYOCFd0gKcAixB5L/Ax4G+iHtcMeRr4LqrfwxQfR0TnwoEYFW6GiZh8VwavWKQo/uHAZ4DjiIf/ZAT4GaqXCTwAmPTqTVGPadZxM0yEhF2lvYrIe4BzsfFccaAXuATlpwjDtXAIxgUnmAgIV3RYl5vShsi5wGnYYMd6Jw/ciOqFxks95JuQdLZxl18T4QQzx4Sd7eB5oHogIl8E3kI8HI8DwEWoXoVIU80qO+MEM4eEXW2Il0K1eCTwVeCVUY9phjwM+gmM3oKICbLx9qVUQxyebA1BvjuD8ZOoKbweuIb4iOV+4F+LqeTNAk0tFnAzzJwQdrWV4gLN64CrseH2ceAe4IOgWRCadRm2M04ws0zYlQERgFcA3wE6ox7TDPkD6Kkgazw1pBrQCVkJTjCzyFjnUopeApQVCNcCR0Q9phmyCjgZeABVmn0ZtjNuDzOLFCUByt4IlxMfsTyF8u8gD2CME8tuOMHMEtYpqXsgXAy8OerxzJBh4PzNr2u7HaMEvW4ZtjtuSTYL5LsyoEVPvcQ5wPnEJwTpClE9WyHvZpaJcYKpMWFnO/geKMcB12LThuPAnSjvBJ4Jss4aNhluSVZrrBd/P+AC4iOWzSifR3hGVKMeS13jBFNDwq4ORDUJ8nFsdmRcuE4wt4ES94zI2SYua+u6xzonBbWFKU6OejxlsB70ShUpBD1OLNPhZphaIR5gOoBzsBUl44AC31FJrsOtxGaEE0wNCLsyqO8DchJwVNTjKYMsqteLKTh/ywxxgqkFIkjRLMdWdonLNVXgOhL+k1I0UY8lNsTly61bwq4MOjYmwPuAg6IeTxk8iupPKRrSa5yDcqY4wVSLCJJIvBR4R9RDKZNb1RQeRd3sUg4TWslGujpA1LMbQTEtzpE1IWFXBj9YQDEcfBfxyccHW+D7/4mfVBeyXx4Tm5Wt/79dRN4B5MLuzEpUnxC8fFEMrT1uCgdAhGK4bX+QuM0uq0BXRj2IODLhkkxQROnHZtudCPJrxLtJhY96eIeF3ZlWPeIohrvj4siuPWFnpvRgkbcSn4SwcX6DeFtwq7GymTSWLFzRBp6PjbiV9wBnAfsC/dg6VLcBt6Ham5q/aCjctonWJqhLteP62BJJSxD5BfCqqMdTBptRPR6Re91yrHymDb4MuzK2aY8vh4B8GngrtomNYnt4rAR+Dfxe1KzD88Ji0dDawKHhulcr+aULAN4O3IDtSRIX7gD9P8Cg8+yXz7RWsiCbQ/w8qKxCOQU4E9sWTbCNa44Bvgj8RsX7sSrv90T2DTszXtjZEfX5zQrhkvmoMT7wNuIlFoB7EG/QefYro6zw/rArg6IiIkeBfA7bIm530RWx/QZ/CdyE6kogLBrDvNWNUfQt7O4AdDnIb4mXdWwE+EfgVrccq4yy82HyK5aifgJUOxA5C1u1cbLmPpuB24Efono74m9VU6QlxhGxo53tGM8D69W/knj5stYBbwSedoKpjLK/7PTqTaVyO9KHci5wOrZ77UQswT7RrkfkZjCnibDX8N4+4cFLoz73ijAioJrCph3HSSwAvaKaw+W8VEzFX3iQ7UMwo+mhvmuxXu47p/pz4DXA1xH5lbek7QwS/l56zELrJI0TIiCyH/CyqIdSAX9VT8bE6aViqnpCprP95FszYBt8vhv4Pran4WT4wKHAZSC35PuC0wXtMIfuzUhnJuprMS16wI6o/aOAvaMeT5nkgQdQXJJYFVS9pAiyObtEU30S1Y8CnwO2TfM2H/hb4MuI3DJqCu8TkcVhdwfhivoVTr4ljZiiYGfLmrWyniMGQB/CmceqomZr8FI+xXZRvRg4A9uZajoS2FTebyD8N/A2PA1KTsG6RMVrJ57LsadQck4v1VHTTWuQzaFCAQrXgJ4CrJnhW1PAG4AfIN63ETki39nh5ettf2P3LwcRn3Z6O/MomMGoBxF3am7lCXpySBHFS/4WeA/wxzLePh+7F/q5evynCpnwkA5GO9ujvk7kOxeP/3oY8WjUujvrkURR3BRTFbNiFk2v3oQWxgDuR/Vk4NYyD7E3tgDeT1COM0Ii6mWaShIpGo/4lHzdGYPt8ULaFRWvilnzI7T05qCQB/HWo7wfuBHKio/1gVcDP0S8zwN7jXTvyWjnkmiulAjqewuJT/X9nckzsz2lYxpm1fEWrNlKekhB9GlUTwe+CxTKPMxi4CxEbhLMG434XoSzzTLgRVF9eBUMAs6WXANm3VMtj/ZRioodQPUs4JtM7auZ8DDAq0CuR7xPAovyXR0MzX0+zv7Y6IW48RzoZrd7qZ45C+0IsjkQtgp6LnAFMFrBYTLABYh8T0W7NZmwhb9nmdGX7tDIgViLXtzYhDLkysBWz5zGQgU9OVQZRPUzwGXYtXW5JIC3g9yUGNMTFPzZXqJpwmc0tRDggLm8XjVkm0DoKs9Xz5wHD5ZmmiHQzwGXUplowJY0uhqRsxDmh92zKRohnd+SJp7+F4CtBgrGNWuomkiibUt7mhHQi7HJZ5WKZjFwPsiXUdkr7O5gtLv2PhuDYPDmYzf9cWRzS0+f88HUgMjC061oZATVi4D/S+WiSQGnIFwDeojBsz1aaoh18LMH8dzwAwyOHLoXnqtBVjWR5nMEPX0gMgJ6EfAlKjMEgLWiHQPyA9DXjxY3Mwv7mqXE08MPMCIYvKJbklVL5AlQNtJZhlEuBL5K+X6anekG+X4q2XaCoFLTfY2yBGiN9mpVTIhCYq3z8ldL5IIBm4yG6LCoXgBcTXkRAbvzYuAbKt5JGLywVgGcwmLi209nJOoBNAp1IRgomZyFbaCfAq6jusSNDuDLeHKKh/Fr5KtZTPxyYMapZtZ27ETdCAZ2GAI2o3o28LMqD7cU+KIR7zSjJEaqF82iqK+PI3rqSjAAEhoQyYGeydR1AmbCIuBi8eTDGElUUietePAeqAjAvKivTRW43X6NqDvBpNePb0zlMeDjwOoqD7kHcKH4fETEJMOu8kzOBS8J4gO0RH1tHNFTd4IBazmTYhE8WQn6caoPTV8AXKDinSqqZUU7q3h46RaIt2CSUQ+gUahLwQCkVw+gxnD3ttxvgXOZvrDGdCwALlTPf5dnCjJz0QipgScgvhYyiGfAaF1St4IBaOnJ8ZoFGUTN9cDlVG/tWQJcavzE8eCRn6GfJkzNE+JrIYOSYHSvOG/D6oO6FgxAOptDkQKqX8JWyq+WZSCXI+aVikyfHiCA78XiWk1BSkUoLopb3fT6IxY3gS3hJNuATwN31eCQy0G+gnKgijDaOXUimmgRbJH1uJIGxmtCO6ogNlewFGn7BHA28FgNDvlyhMuADuNNsdpSGD34KCXezr+Ueh7qrMtVExvBpLM5wIAm/4StKLO9Bof9B+C/UG2Z3AigJB7rhfLTquuJlPzbV1AX3l81sREMQNDTD4yB0RuAq6i+7qkApyByKpNYzjxVpFAECKM+/ypIy1WnifNfVk+sBAOlQE1PRkEvBX5Ti0MCn8JPvgnx2N2xmZQ8YvNIhqM+9ypIIZ5TSw2InWAsBpAc6HnYbmfV0gFcjJrliKD7Pv8fkh2kNJENRX3WVdCqSEy/6/oilhcx6OkHNeAn7qe6ugA7czgi56MsyC+YcD9TreM0Slo8EU/ETTLVEkvBAATZfigUQfkB8N81OuyJiJwmUpDwhbUBthBf03IAJFznseqJrWBgPPGMIeAiYG0NDpkEPqEkj54gEuA54mtaDlRIqptgqibWggHwFNRGNF9CbTIL98Samjt2ex5vJb6WsgCVJE4xVRN7waSyfYgqqN4E/LxGh30tIh/2isbbKXRmK/G1lAUICWdVrp44R+DuIP3sCPm9WoexNc6OAvat8pAe8CHj+/+LbZsOdtM/SDxrkwWgMw7x37p8Ry/PXSQmRhSBhQ9vjfp8IqMhBCObBgmXtZJ4154rCzds/DpwMdVHF7cD/4nqKmATMITIALa+ctxIgaSn+oP8iqVoIgnGLEXkddgHz6Ld/qwPuCvsztwDsn10dJA91sV10q2MhhAMWANAeAOAfg/kWGwLwGp5AyKn9vf0faGts31YRJ6N+jwrJMkUCXDhinY05UPRHInIxcBrmTyHZhjk56DnpdILHg1XzCdY3TydNGK/h9kFAyAD2ELntfCb+MDp7d0dR+L7BeCpqE+xQqYUDL4HRT0I5NvAMUydcNYKvBPkaxja8ZtrY9RQggl6+0AV1PwP1VedGWcf4JNiigE2WjqOTCqYsDODly8I8AFs/86Z8maEfwJhpMw6CXGmoQQD490BvDxwJXbNXQvegngnYNMK4uiLmXyG8cCk/QzwpjKP6QPHoyZopgiChhMMYGcZY+6jdhEAAfAf2OVILdIK5hqfqctELcU2qyqXFwHzmylroCEFE2Rz4HlF4BpqN8scBnyWeNZXFiYVjAAySmX5PnniOeNWTEMKBijNMvpXym95Phk+sJx4VmDxgPkT/o8CykZgfQXH7RFjtjXPgqyBBZPO5sCTArZwRhyXUbVmYsGgIGwHrqW80J8B4Ifq+ybVRG1nGlYw9qmnoPon4C9Rj6cOmKdA8eBdC37YAiMKqjcC32JmS7Mh4BI15k5VRZwfpjFQBcTbBvwi6rHUAfMQn8IE8Q+lIvDDKOcBnwAewEZnD+322gLcDbxf0K96nhRbss0jFmiCJO+wuwPgUGw6c42axcSSK1m+4MOs20bQO/FNbgNNVRTJILKfKjvCacT+MwT6aHq7t2W0VUn31sqeEh8aJjRmGtYDDwJvjnogEdLK2ucETyY1AtvKPCjWsth8apgBDb0kA+y6TGQY+GPUQ4mYeSpN8H3PMg1/AYNsrrSZ4X4qbzrbCLSKE0zVNMcFtL6Gh7Fh+s1KICKJht+0zjLNsYexy/YcyEbimQBWCwKUJDFvEDvU3UagPmMYH5E9QBajLEYIAEUZAd2CzZAdRChSEII1tdmSNYVgStvcIZXmnmF0Bt93eHAbjI1BkF6KyIHYRDoDPIvqQ6RSg2Z4O61rt87oQ0tWyiTwN8B+2NCibcBjomaDIsVgGtN02N0G+AgaKLJiTDgavJcBLwE6EOZjz00RxkCGYEf0wp9IcGfYlVmHJ3nGCgRrKr8NmmKGLrUeTyLcDBwb9XgiYjXoG4A+63fZla3LFxKk0wCLEHkncDJwMPYGV2x69krg26j+AhiZ6kYPuzJ4asR4/uHAh7AWygw2xGgMeAa4BeWbY6TWpiRPeoJxlcr3Boi8ETgFeA3QxszvXQVywB3A90X1NiCfrtB/1CSCyQAsQORXwKujHk9EPIrNpHw66Hnh8qQ0EyzDJt/9E5PPRsPAlaJ6vsLgRKIJuzOIqqh4J2LrLExVY6EXOB3hDowyfrywM0PQmyPs7jgEOAt4G7aLXDVsA36ETWF/XDCke/rLOsCcC0YPaiOf8sE+KVaA3obq44I3pjJG0LO55p9pbwbdH+R/sMuCZuRJ0NeCPL67YOwMrPMQ+Rrw3hkcqwBchDEXILsuqUY6M7aMs3IMwrXMbM/YA7wDWBP09BHaenAJkBOBC7BBr7XkXuAMhHt3FulMiNBKpsPAR0FuR7xrVDgJEvvku9q8fA0z+J4/lrwGm7/RrCQmKoSR78qUIvzlLdibdobH4oOIvJzdksdEANVFCGczcwNLN/Ax0IR9uEkAcia2Q0OtxQLwCuA7qL4CEcrprD3ngpF1A1AsgJe4H+UD2PikdwLfA/m9iv81Fe/4sDuTyXe3SzjDPpSTobYG997AaTSJkWMSEsALBKMoqEljxVJOp+gMIifgy/ge0SICwiuAvytzfP8AcgBqEtimWZ/Btox/fqg2mnoTNgphK9WV7u0EuRx0v3LqtEdyAwWrNxF2ZhDf+4Oqvg/7JDkMW8LoQOC9IA8pcgfwu7ArsxK0D5FiOswj65+b9jPGDmynGHigugzkIuCVUZxrHZEATb9gFW5vlnagq4JjHkHRzCuV6yW/or2UfClHUH6i3TLg7Yi3EDgDm+UK1thwN/BboBdlANE8yHxgf+ye9M3AAZQ/ARwFcjaq/x52ZUZnsjSLdNMfdmVKcziHA98EXjbRnwGPY0P07wT9C8qTAoP54tiYLx7zVm8i7J5HobA/XmKjeCppRDqw+6TTgFdVcDEbjUHgOOCenfcwpf3dCpDbKD84dRXoMUB/0JOz36fng5ovAR+vYIwh1gTtY03ZdwCXiuqd6jHs54XkuufHPtLZAYonPvtiv+cPAgvL/MzngH8Bfi3FIunVA1P+caRLlCCbK23wZCXwPuAbvNCKFQAvLb1OAtmMsEFhQyqRehrYFHZ3DAGpRCK3CLw2hL2wT5wXEc8MydlgwiWZRXwqe6D47PzQFQX1AFPpg3h8VhkDvoPyWYQ+LTLhxrzFRkubsDvzGMp5iDyEtcotKeMzFwKnieqdeN60VQkjX9MHPTnyne0Yz+sR1VMQ+TqTVzDxsDb4Nuys5Jg5PvbpHQeuF9VPKmybyGe0O3Z2ay+IKXxfvWQG+Bzl3dtHK3Ikwp3T/WFdLFPSvf34pgAi61F9P7aoeBPVIpkTPOIhmF7g8yqyrRxzb5DtR72kAa7Gmo3LYRHCW9Tzmc7IVBeCAUj1bgKKIPIEdi16HfFtYFSv1Pvy1ADfQlhf2eNSwdYauLGCNx8txcKS6T63bgQDEPQMEPT0gepGVM8AvkZzh+TXEqH6Au2zzQbgV2ipWVaZ7LR8uwco1wO+HJHlTFOUsK4Es+PEszkQ2Yrqudj1qKv6UhvqPRRqFaobqlqN215BGyk/lWMRcMh0f1SXggHsTIMMo/oFbCxRM0ca14p6F8w69fxRqaYXpwiIjFJZGsPB+D5h5+Se/7oVDOyYlsdQ8y1sxOuGqMcUc+rdkDIgakhnywuInOQ8KznX/aUwlhRv8udKXQsGxu3vYkZ6+m5C9d+wVhRH+SiVlYOdS6ovO1upVCwZRVqmmuDqXjBgRdPS1Qbi3wa8B6a3lztegGJrIdczUS8ZFyPSOtXGPxaCAQiyA2AKAH8F/hVrOnRm55lTxFkcp6OV56MNJiQ2ggEIevtJDwHoE6h+ELic+HY2nmvGiHk+/xyQopEEAyCP9o3b27egOl7adGPU44oBITYA0zE5PtM4d2MnmHFKxoA8Ra7CZgn2RD2mOscJZno8ptFEbAUD1uysGBVPfoPqvwC/pNQa1vECBoHh+rcsR86UhodYCwagZXU/ZnQMRFaXzM5XYCvNO3Ylh+qg00t1xF4wAC1rNhH09CFCv6DnYDP2nox6XHXGMyhh9JbbeNMQghkn3ZNDDXkNC98tLdHuxK1Bxtmgvm88dZb4amgowQAEvTkSgagifyiJ5qu4JZoC60QNqd6Bqg/WzDScYACSPQO0ZPtAeBbVs7FxaA9HPa4I2QasjXoQjUBDCmacoCcHQn5kZNl12GqOt9BkbbJLbIQqw+YdQIMLBqxoWpLPAPoAqidj6101V2NGWIdLj6gJDS8YgGBNbjw6YDNqLsGW1bmL5vHZ3Kd4+aryTBxAkwhmnCCbg6IYhNtR/WfgUmzlzUZmGLhX0FrkmTQ9TSUYKM02q/oANoqaT4O+G/gTjTvbPI66sKFa0XSCGSfI5sBQwJNfgZ4AXEhjdg6+GzO2EW3U58Hc0rSCAUiv7id4sA+UZzHmfOAfgV/QOHkjIXArflIDtxyrCU0tmHGCbA48z6Dcg+q7gY/RGH6LHlTvwW32a4YTTImgp48g24eHbkuPDV0F+lbgK8TXHKvAj/Ck38WP1Q4nmN1IZfuRtdtBWS/GnAl6InaZFkY9tjJZhepNtsNWI27NosEJZhKCbA71KAD/C3oStp3CSuJhTcsDV+D5T+KCLWuKE8wUBD39BD05RBk0IteivBU4D3iI+o4zuQnVGzFFgt64rijrEyeYGZDO5mhdtRHQp8XoJcDxwOexjZ7qTTj3ovpZRLaXUf2+0mpeu7xHFFQMFR4Lajt7V3o+U77PCaYMgmyOdG9OMTwiRf0MqscBXwCeoD6E8yDwEUQe0RlbxhTQISrbow3u/D7P7LjftlZwLKUmURcKaJ7K6nFPW/fACaYCgt4+0qtzRmGtqn4KOBZbNP0hotvj3AWconCfqNIy09nF3uP9wCMVfOZqr1jYPh6jllw9QOn3LOX7sraiuqb6y6BQyG+nsgqpj6Cam8oM7wRTBS3ZHC3ZnBHVtaLms6DHAudgiw3OlfNzBPguyrtBVooWSZfRiAgA8bYDN1Oe2IeAm42fNKlw2+7/90fKv2H/AGSr9Rmls/2QDBRr2SxnllHgZhVvcKqOF04wNSCdzZHO9qsWeczki19EOQ44GfgJNtxmNpZrBrsE+xCqH0HYEPRstBVCyyDI5kotIvgRdpaaKT9D9fegyMPPV6CVYgE871lsb5+ZFlnsB65AZKhaj5HAeMuL24GflvHWu1C9QdSQnqJNoPNozQJhdxuKj6imETkI2xb7TcCh2Bbf1TyoxrBP7x+BXp9etM+T4ZYNtFQR+pLv6kDtnXAEtuXdYdO85XeofgDhcRkzpNfuKtKwuwOUNMJ5wJlMXU1yC3COmLGrVXxTixCesA1Y1gHoviBXAX8/zVseBE5VuM9TnXKGdoKZZYa62mhNLSA/OtSKyAHYZrYvB7qBF2Mb3LYwuYiK2KXFs8CfsR267pBicSOeR7q3Nrlw+c4MxvMQ1U6ET2JblC/l+XvElMZwo6heruI96ZsCyUlqBITdGVBaEDkJ+AhwMLtWlRwB7gMuE9VfAoWyl5JTMNbZTtHzQPVFiPwH8M/Asp2us2KjOG4F/QLi91IsEPROLVgnmDlmpDMDGBHx5iGSAfYB9sN+mYuwjVuLWOfjVuAprPl6A0b78aSQfmQzMlz7zhXaDXntQNC0Qjcir8KK2gDrUb1HrGFjRjf3aGc7nqoUPG/P0rEOBeZj2+n9BeXPxdaWLf7Q0LQ3aqXkuzIACYUDEXk18BKsaDYA96BkgXxa+hCXBOFwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDQY/x8QLEtwly8ONAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMC0wNS0yNVQwMzoxMDo1NC0wNDowMAWjS6oAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjAtMDUtMjVUMDM6MTA6NTQtMDQ6MDB0/vMWAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"bb405265-40cf-4115-93e5-a332c1968d8c\": {\n    \"name\": \"ID-One Card\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"2d3bec26-15ee-4f5d-88b2-53622490270b\": {\n    \"name\": \"HID Crescendo Key V2\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"30b5035e-d297-4ff1-010b-addc96ba6a98\": {\n    \"name\": \"OneSpan DIGIPASS FX1a\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAADuCAMAAACnBt2RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMi1jMDAwIDc5LjFiNjVhNzliNCwgMjAyMi8wNi8xMy0yMjowMTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyYjFkZTQyNS1hNmVkLTAyNDUtYTY0Zi1iY2Y5OGViNGI4ODciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDBFQ0RDNDc3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDBFQ0RDNDY3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjQgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6YjRlMWY1MzItMzZiOC1jMDQ1LTgxOTMtMTBhZDg5OWQwYjVlIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6MjYxYWJiY2YtYzg1NC0zMzQ1LTgyMGItNmUwZTYzNTI4MjVjIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/BQPnwAAAYBQTFRFWltjLS0wW1tdTE1TZmVmIyQji4uSbWxtVVVY6OjoGxwbMDEyRERLSkpMLjAxkZGVbG13NDU4/v7+urq8ysvNenp78/Pz+fn5UVJWLC0umpqcc3JzYWFiqqutioqNsbKzQUJGlZWYOzxCwsPFMTI0NDU2g4KEoqKlOTk+rq6yKiwuhISKZGRrfHyB0dLUExQTNzg5MjQ129vbdXV5nZ2gKCkp/Pz8paWozM3PODk6tba4T09TPT4/KSosYWFlxcbHCgsKbGpsvb7AR0dMV1haICIia2pqJSYmaWpvPj9DJicqx8jKaWloS0pXZ2doz9DSXl9muLi6b3B4Ojs9l5icv8DCR0dQj4+UHh8dZGNkh4iMXl5g1tbXFhgYr6+xZmdvZ2ZnaWhpb29wZ2hrHyAfh4aIY2Rmf3+Ap6iqERIQDg8OLi8vFRYVSEhUHR4eMjMzKisrDxAPamlq3+Dga2trJicnGBkY+/v7j4+RbGxwY2Jjl5eZLzAwKy4ua2pr////S5YpOQAAAIB0Uk5T/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wA4BUtnAAAfWklEQVR42uydi1vaSNvGDQSDxMTiETxGG9TSqrBILaL1AHxUXU/FE2V1gcVjlFAK1trg/uvf80xArQYI9t2Vva5OIORE+HFzzzMzyZA0/P0fTA2/oH9B/4L+Bf0Luub0tum3L5XSb01v6w76S8QUKpNS6kvaNV5n0F94J1+onJyh+S91Bd3qcYqFAlUROhM6jdUV9FvOyldV2rSvtNYTdJdgZfnKQoPS+/a39QT9tVkUqwhdyITP6ww6IIrV7FGoO6WF6p7OmP6L0HVnD8Fa3dP1pnSXDqWdoXpTekmH0uH9Qn0pveTWkRHP6w1aV0Ys1Jk9qhcuTlOdeVqP0s7QfuaXPX4+5Il6ann/QXuY6jAj/ufqHvqU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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAADuCAMAAACnBt2RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMi1jMDAwIDc5LjFiNjVhNzliNCwgMjAyMi8wNi8xMy0yMjowMTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyYjFkZTQyNS1hNmVkLTAyNDUtYTY0Zi1iY2Y5OGViNGI4ODciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDBFQ0RDNDc3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDBFQ0RDNDY3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjQgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6YjRlMWY1MzItMzZiOC1jMDQ1LTgxOTMtMTBhZDg5OWQwYjVlIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6MjYxYWJiY2YtYzg1NC0zMzQ1LTgyMGItNmUwZTYzNTI4MjVjIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/BQPnwAAAYBQTFRFWltjLS0wW1tdTE1TZmVmIyQji4uSbWxtVVVY6OjoGxwbMDEyRERLSkpMLjAxkZGVbG13NDU4/v7+urq8ysvNenp78/Pz+fn5UVJWLC0umpqcc3JzYWFiqqutioqNsbKzQUJGlZWYOzxCwsPFMTI0NDU2g4KEoqKlOTk+rq6yKiwuhISKZGRrfHyB0dLUExQTNzg5MjQ129vbdXV5nZ2gKCkp/Pz8paWozM3PODk6tba4T09TPT4/KSosYWFlxcbHCgsKbGpsvb7AR0dMV1haICIia2pqJSYmaWpvPj9DJicqx8jKaWloS0pXZ2doz9DSXl9muLi6b3B4Ojs9l5icv8DCR0dQj4+UHh8dZGNkh4iMXl5g1tbXFhgYr6+xZmdvZ2ZnaWhpb29wZ2hrHyAfh4aIY2Rmf3+Ap6iqERIQDg8OLi8vFRYVSEhUHR4eMjMzKisrDxAPamlq3+Dga2trJicnGBkY+/v7j4+RbGxwY2Jjl5eZLzAwKy4ua2pr////S5YpOQAAAIB0Uk5T/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wA4BUtnAAAfWklEQVR42uydi1vaSNvGDQSDxMTiETxGG9TSqrBILaL1AHxUXU/FE2V1gcVjlFAK1trg/uvf80xArQYI9t2Vva5OIORE+HFzzzMzyZA0/P0fTA2/oH9B/4L+Bf0Luub0tum3L5XSb01v6w76S8QUKpNS6kvaNV5n0F94J1+onJyh+S91Bd3qcYqFAlUROhM6jdUV9FvOyldV2rSvtNYTdJdgZfnKQoPS+/a39QT9tVkUqwhdyITP6ww6IIrV7FGoO6WF6p7OmP6L0HVnD8Fa3dP1pnSXDqWdoXpTekmH0uH9Qn0pveTWkRHP6w1aV0Ys1Jk9qhcuTlOdeVqP0s7QfuaXPX4+5Il6ann/QXuY6jAj/ufqHvqU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\"\n  },\n  \"cb69481e-8ff7-4039-93ec-0a2729a154a8\": {\n    \"name\": \"YubiKey 5 Series\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\"\n  },\n  \"0076631b-d4a0-427f-5773-0ec71c9e0279\": {\n    \"name\": \"HYPR FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACNgAAAjYCAYAAAAADILPAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABANpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjVEMjA4OTI0OTNCRkRCMTE5MTRBODU5MEQzMTUwOEM4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkQ4RThERjcwNzM1NzExRTk5MTU1RUU2NEM3MEEwNDExIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkQ4RThERjZGNzM1NzExRTk5MTU1RUU2NEM3MEEwNDExIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTBhMjJkMGUtMjUzNy00ZjU1LWEzNTctZjE3Yzk0Y2ZlNTkxIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OTg5YTAzY2YtNjlhZS0xZDQwLWI0OWYtOWQxMTFlMGU2YjM1Ii8+IDxkYzp0aXRsZT4gPHJkZjpBbHQ+IDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+UHJpbnQ8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pl2Dyx0AAJydSURBVHja7N3/bVxVGoDhE0QBKWEaQEoJLiEdrDvYNICSiAIQFZBUsNkKGCrAiAIYKiBbgXcOMxP/UPISknjGnnke6Uj2hD/gs3WUe+/LuY8uLy8HAAAAAAAAAADwfl8ZAQAAAAAAAAAAfJjABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACC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DQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAD/z94d1caNhmEY9UWJFEIgBEIgDIQwaBkEgpdBIHQZDIRACIT9LY+0lXb1tE1mkrHnHOmT79/rR/4BAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAA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AAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgC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NgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAARtX8nE+AUck4AAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACNgAAAjYCAYAAAAADILPAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABANpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjVEMjA4OTI0OTNCRkRCMTE5MTRBODU5MEQzMTUwOEM4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkQ4RThERjcwNzM1NzExRTk5MTU1RUU2NEM3MEEwNDExIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkQ4RThERjZGNzM1NzExRTk5MTU1RUU2NEM3MEEwNDExIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTBhMjJkMGUtMjUzNy00ZjU1LWEzNTctZjE3Yzk0Y2ZlNTkxIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OTg5YTAzY2YtNjlhZS0xZDQwLWI0OWYtOWQxMTFlMGU2YjM1Ii8+IDxkYzp0aXRsZT4gPHJkZjpBbHQ+IDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+UHJpbnQ8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pl2Dyx0AAJydSURBVHja7N3/bVxVGoDhE0QBKWEaQEoJLiEdrDvYNICSiAIQFZBUsNkKGCrAiAIYKiBbgXcOMxP/UPISknjGnnke6Uj2hD/gs3WUe+/LuY8uLy8HAAAAAAAAAADwfl8ZAQAAAAAAAAAAfJjABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACCwAYAAAAAAAAAAILABgAAAAAAAAAAgsAGAAAAAAAAAACC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DQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAAAAAAAsCCwAQAAAAAAAACABYENAAAAAAAAAAAsCGwAAAAAAAAAAGBBYAMAAAAAAAAAAAsCGwAAAAAAAAAAWBDYAAAAAAAAAADAgsAGAAAAAAAAAAAWBDYAAAAAAAAAALAgsAEAAAAAAAAAgAWBDQAAAAAAAAAALAhsAAAAAAAAAABgQWADAAAAAAAAAAALAhsAAAAAAAAAAFgQ2AAAAAAAAAAAwILABgAAAAAAAAAAFgQ2AAAAAAAAAACwILABAAAAAAAAAIAFgQ0AAAAAAAAAACwIbAAAAAAAAAAAYEFgAwAAAAAAAAAACwIbAAAAAAAAAABYENgAAAAAAAAAAMCCwAYAAAAAAAAAABYENgAAAAD/z94d1caNhmEY9UWJFEIgBEIgDIQwaBkEgpdBIHQZDIRACIT9LY+0lXb1tE1mkrHnHOmT79/rR/4BAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAA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AAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgC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NgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAARtX8nE+AUck4AAAAAElFTkSuQmCC\"\n  },\n  \"d7a423ad-3e19-4492-9200-78137dccc136\": {\n    \"name\": \"VivoKey Apex FIDO2\",\n    \"icon_light\": \"data:image/png;base64,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cAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6R0lNUD0iaHR0cDovL3d3dy5naW1wLm9yZy94bXAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDo2OWExYmMwNS00M2JkLTRhMjQtOTQ3MC01NGM4YTI3YzcxYmMiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDJmZGJlZmYtMTJlOS00Mzk4LThkMDQtMDU0MzExYWZlYjE2IgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZGNjNjkyYzctYjJiNS00NWFlLWFmOGQtZjAyZWUwYTI5ZDU1IgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iV2luZG93cyIKICAgR0lNUDpUaW1lU3RhbXA9IjE2NjAxNTI5MDEwMzU3ODAiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zMCIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDphYjljYTRkNC0xMDQ3LTRjZGQtODAyNi00OTI1YjY5ODNjYmMiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkdpbXAgMi4xMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OndoZW49IjIwMjItMDgtMTBUMTA6MzU6MDEiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+6HMtNwAAAAZiS0dEAP8AAABBMvwN9QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+YIChEjAPBJR7wAAAkDSURBVFjDrZZ7bFP3Fcc/92HHdpz4FcdxDEnIC5KQ8AyQUJJQCpQWNlhbtI2qa9Vu09ROk/bf1D/2R6f9W01bJ23a1kntimgR7WhXSimlkJIGSElDAiHk6RDHeThx7Nj4fe/+IJiYAK2mff+6uufc3/mec77n3J+gqqoKoKqQUhSSKYVUSiGRSuIL3MLrD+H1h5gORvCHIgTCMW7FEiiKSlmBmcfWljI84efSoBedRsZs1JFvyqay0EaZ04qIgiiKiJKILIrI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\",\n    \"icon_dark\": \"data:image/png;base64,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cAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6R0lNUD0iaHR0cDovL3d3dy5naW1wLm9yZy94bXAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDo2OWExYmMwNS00M2JkLTRhMjQtOTQ3MC01NGM4YTI3YzcxYmMiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDJmZGJlZmYtMTJlOS00Mzk4LThkMDQtMDU0MzExYWZlYjE2IgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZGNjNjkyYzctYjJiNS00NWFlLWFmOGQtZjAyZWUwYTI5ZDU1IgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iV2luZG93cyIKICAgR0lNUDpUaW1lU3RhbXA9IjE2NjAxNTI5MDEwMzU3ODAiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zMCIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDphYjljYTRkNC0xMDQ3LTRjZGQtODAyNi00OTI1YjY5ODNjYmMiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkdpbXAgMi4xMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OndoZW49IjIwMjItMDgtMTBUMTA6MzU6MDEiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+6HMtNwAAAAZiS0dEAP8AAABBMvwN9QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+YIChEjAPBJR7wAAAkDSURBVFjDrZZ7bFP3Fcc/92HHdpz4FcdxDEnIC5KQ8AyQUJJQCpQWNlhbtI2qa9Vu09ROk/bf1D/2R6f9W01bJ23a1kntimgR7WhXSimlkJIGSElDAiHk6RDHeThx7Nj4fe/+IJiYAK2mff+6uufc3/mec77n3J+gqqoKoKqQUhSSKYVUSiGRSuIL3MLrD+H1h5gORvCHIgTCMW7FEiiKSlmBmcfWljI84efSoBedRsZs1JFvyqay0EaZ04qIgiiKiJKILIrI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\"\n  },\n  \"ba76a271-6eb6-4171-874d-b6428dbe3437\": {\n    \"name\": \"ATKey.ProS\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"97e6a830-c952-4740-95fc-7c78dc97ce47\": {\n    \"name\": \"YubiKey Bio Series - Multi-protocol Edition (Enterprise Profile)\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"ee882879-721c-4913-9775-3dfcce97072a\": {\n    \"name\": \"YubiKey 5 Series\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAbNSURBVFhHpVd7TNV1FD/3d59weQSIgS9AQAXcFLAQZi9fpeVz1tY/WTZr5Wxpc7W5knLa5jI3Z85srS2nM2sjtWwZS7IUH4H4xCnEQx4DAZF74V7us885v9/lInBvVJ/B4Pv9nu/5nu/5nvM556fzA/Qv0Hb/IrX3VFKPo45cnm4inUIWYwLFRmZQUuwjFG/N1iRHh1EZ0NRVRudqt1Bd+2nSKyS/Ohys0+lk3e/3kQ9qvD4ZUta4VVSUuY0eipyiThAfocoORVgDuuw3qKRiAd3rbcEtjTjYIof6WaHsCmzVPWCMx+cgh8tLqWMKaMWsUjLqo2RtJIQ0oOzmerpQu4esZgsONkGxH7d0kdvTT17s4OMU7VI8ZhjgGaM+Aq9iENu8Pif1udz07MwvKWf8GlVoCEY04PC5WdTaXYFbR8vNvL5+3Kgfb5xNMya9RamJiynaMlGTVtFlr6ba9u+pqnEX4uMuRRgjSYEhrN7utFFe6lqal7Nfkw5imAGHynPpbk8VmY0xstnptlFCVCYtzTuBN83QpMLjTtevdPzSUnJ7e8mkjxZ39fXbKDfldZqbvU+TUgGnBVF6fQ2iPHg4W16UWUwvzbk16sMZE+Pn0pvz7JSeuAyes8lcpCmaKuo/p+qWr2UcwIAHWrvP0YEzhXAtLAbssHhp7iGamvyijP8ryqrXUWX9XoowxyAufNBrp43POBFXZlkf8MDRiqcpyowAwpuz2x+fWvz/Dtde9smszygtcR6C1wbdzBl6Olq5WNYY4oGathJMrkTEx0jARSHAVs+5rYkQNXb+QgfPLsQ6gXyInsreQfmpm7RVFYfL86n1fiUOkYvShkUPxvbukzoy6K1ihM1ho3XzW6EvSfXA+dpiWGaWd+doXzLzmGwKYFLCAsRAlPBAhMlCFXU7tBUVPr8HgVcJHWq+F00plr+DMTdrP4zvxY11kNMhxT+SeTGg+d4V5LQJityUGJNB8VFZsjgYBZM/II/XCTkj0qyDOpF2AVQ17CIjUp/DnT1UkL5F5gdj+sS1wg1gE3gigm60fCXzSnPXbyAPbIXv+IDpE16ThaHIS9skyhlmME5F3cfqAKhq2C0E5PH1gYaXaLPDkZG0HDJOnKWHp51I0z5SOux8e1WAuZzdHQrTkp8TmjXoI+la0wGZszubqbO3ifQ6A/W7vVSYsV3mR0JKwkKc4WHiBkmR8I3CCgI87oOL4qzT5P+RUJBejEOgAPK8hYPzatM+eITp2IO9yTQmeromPRxx1qxAcsile/ubSeEbcWQGYECghcLY2HyKjogjH25hMpjpUv1Ougli4eh2eRw0O32bJjkyuCgNzg0vzlYMSiSs0uoo4MG7hMOjCEaX1yFE0nSvjBzuTnEpK86Z8IoqFAIubw8kg9ArEaREWSZI+jH4Xbp6g9E9EnJT3oaRzDN+MUJBQDHn56a8oUmEBusOxBs/N5+tJEbPkAFDj8UGvOs/IWvcSglGBhvS7/FTYfpWGYdDY8fPAxWSA35sTC4p4+Lm4AaqIoPeQtfufK6Jh0ZhxlbsUXOSmXNifD5ZTAkyDofbbcclxnA8WNAqxCbRNykhXxQpaDw67fXUYbsiG0Khtv2oeIvh8rhQMYOcEAqXG/eI+zngOc5yxr8q82IAM1c/FLFOplqu5eFQXrMZzGcVCjYbLWG5I4BT1euRrlbxtNOtMitDDEhLXIIynAAvuOEWE3X3NdAft94VgaG42XIQt0ZX6PeCE/qQFe9rK6Hx7YU50KvH7fW4fS+q7KKBJxsggBX5pSAGh1jIrVh5zQ6w3RfaahBXm/aCbCZTjCUFUTyWZqW9p62MjJPXVqOrPgMO4Nv74Gkf+owftNVBDQnjFJqHSw17pXvhWW5KZqe/Q49N/USTCAVWoQXFIHBHXXe3FPrUDsuGDmtF/hHKTHpekxhiAOPI+SJq6S6HF4I9YWzkBJTo46iUMzWp8Pir/RiduLxKYsSksV8vLlOQvhGX2YlR0OBhBjC+u/gEcvY0ApK7Yk41NxjPSQnWFHTF66UrjgevB8Cu5a+l2vYSRPtuVDo73hhdMSHnUX7tTjsVZGxAl/WptiOIEQ1gnL29mX6/tR1tmlkYj8W4X+CSjWcUDGY1NpS/C7hSKqiMLM/l2QmSWZ73Ddz+gio8BCENYPQ46qnkzwXUbqvBkxjUQsWfZFgbuo3rAf+wN7jOO90+ynx4Pi3L+0nYL1SchDUgAP4gPV/7Id1q+1HShmuGkIqWRPgyxMFqP8HfjTnjXwY5bQfbJct6OIzKgMHotF/He1egsaxHSqG6wfdmQ5x8NyTFFqBcp2iSowHR3yk5+36hF7vXAAAAAElFTkSuQmCC\"\n  },\n  \"8876631b-d4a0-427f-5773-0ec71c9e0279\": {\n    \"name\": \"Solo Secp256R1 FIDO2 CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"fec067a1-f1d0-4c5e-b4c0-cc3237475461\": {\n    \"name\": \"KX701 SmartToken FIDO\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"30b5035e-d297-4ff1-b00b-addc96ba6a98\": {\n    \"name\": \"OneSpan DIGIPASS FX1 BIO\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAADuCAMAAACnBt2RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMi1jMDAwIDc5LjFiNjVhNzliNCwgMjAyMi8wNi8xMy0yMjowMTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyYjFkZTQyNS1hNmVkLTAyNDUtYTY0Zi1iY2Y5OGViNGI4ODciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDBFQ0RDNDc3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDBFQ0RDNDY3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjQgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6YjRlMWY1MzItMzZiOC1jMDQ1LTgxOTMtMTBhZDg5OWQwYjVlIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6MjYxYWJiY2YtYzg1NC0zMzQ1LTgyMGItNmUwZTYzNTI4MjVjIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/BQPnwAAAYBQTFRFWltjLS0wW1tdTE1TZmVmIyQji4uSbWxtVVVY6OjoGxwbMDEyRERLSkpMLjAxkZGVbG13NDU4/v7+urq8ysvNenp78/Pz+fn5UVJWLC0umpqcc3JzYWFiqqutioqNsbKzQUJGlZWYOzxCwsPFMTI0NDU2g4KEoqKlOTk+rq6yKiwuhISKZGRrfHyB0dLUExQTNzg5MjQ129vbdXV5nZ2gKCkp/Pz8paWozM3PODk6tba4T09TPT4/KSosYWFlxcbHCgsKbGpsvb7AR0dMV1haICIia2pqJSYmaWpvPj9DJicqx8jKaWloS0pXZ2doz9DSXl9muLi6b3B4Ojs9l5icv8DCR0dQj4+UHh8dZGNkh4iMXl5g1tbXFhgYr6+xZmdvZ2ZnaWhpb29wZ2hrHyAfh4aIY2Rmf3+Ap6iqERIQDg8OLi8vFRYVSEhUHR4eMjMzKisrDxAPamlq3+Dga2trJicnGBkY+/v7j4+RbGxwY2Jjl5eZLzAwKy4ua2pr////S5YpOQAAAIB0Uk5T/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wA4BUtnAAAfWklEQVR42uydi1vaSNvGDQSDxMTiETxGG9TSqrBILaL1AHxUXU/FE2V1gcVjlFAK1trg/uvf80xArQYI9t2Vva5OIORE+HFzzzMzyZA0/P0fTA2/oH9B/4L+Bf0Luub0tum3L5XSb01v6w76S8QUKpNS6kvaNV5n0F94J1+onJyh+S91Bd3qcYqFAlUROhM6jdUV9FvOyldV2rSvtNYTdJdgZfnKQoPS+/a39QT9tVkUqwhdyITP6ww6IIrV7FGoO6WF6p7OmP6L0HVnD8Fa3dP1pnSXDqWdoXpTekmH0uH9Qn0pveTWkRHP6w1aV0Ys1Jk9qhcuTlOdeVqP0s7QfuaXPX4+5Il6ann/QXuY6jAj/ufqHvqU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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAADuCAMAAACnBt2RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMi1jMDAwIDc5LjFiNjVhNzliNCwgMjAyMi8wNi8xMy0yMjowMTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyYjFkZTQyNS1hNmVkLTAyNDUtYTY0Zi1iY2Y5OGViNGI4ODciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDBFQ0RDNDc3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDBFQ0RDNDY3MjUwMTFFREI0MTFDMDc5NzM5NkRGODEiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjQgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6YjRlMWY1MzItMzZiOC1jMDQ1LTgxOTMtMTBhZDg5OWQwYjVlIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6MjYxYWJiY2YtYzg1NC0zMzQ1LTgyMGItNmUwZTYzNTI4MjVjIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/BQPnwAAAYBQTFRFWltjLS0wW1tdTE1TZmVmIyQji4uSbWxtVVVY6OjoGxwbMDEyRERLSkpMLjAxkZGVbG13NDU4/v7+urq8ysvNenp78/Pz+fn5UVJWLC0umpqcc3JzYWFiqqutioqNsbKzQUJGlZWYOzxCwsPFMTI0NDU2g4KEoqKlOTk+rq6yKiwuhISKZGRrfHyB0dLUExQTNzg5MjQ129vbdXV5nZ2gKCkp/Pz8paWozM3PODk6tba4T09TPT4/KSosYWFlxcbHCgsKbGpsvb7AR0dMV1haICIia2pqJSYmaWpvPj9DJicqx8jKaWloS0pXZ2doz9DSXl9muLi6b3B4Ojs9l5icv8DCR0dQj4+UHh8dZGNkh4iMXl5g1tbXFhgYr6+xZmdvZ2ZnaWhpb29wZ2hrHyAfh4aIY2Rmf3+Ap6iqERIQDg8OLi8vFRYVSEhUHR4eMjMzKisrDxAPamlq3+Dga2trJicnGBkY+/v7j4+RbGxwY2Jjl5eZLzAwKy4ua2pr////S5YpOQAAAIB0Uk5T/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wA4BUtnAAAfWklEQVR42uydi1vaSNvGDQSDxMTiETxGG9TSqrBILaL1AHxUXU/FE2V1gcVjlFAK1trg/uvf80xArQYI9t2Vva5OIORE+HFzzzMzyZA0/P0fTA2/oH9B/4L+Bf0Luub0tum3L5XSb01v6w76S8QUKpNS6kvaNV5n0F94J1+onJyh+S91Bd3qcYqFAlUROhM6jdUV9FvOyldV2rSvtNYTdJdgZfnKQoPS+/a39QT9tVkUqwhdyITP6ww6IIrV7FGoO6WF6p7OmP6L0HVnD8Fa3dP1pnSXDqWdoXpTekmH0uH9Qn0pveTWkRHP6w1aV0Ys1Jk9qhcuTlOdeVqP0s7QfuaXPX4+5Il6ann/QXuY6jAj/ufqHvqU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\"\n  },\n  \"b267239b-954f-4041-a01b-ee4f33c145b6\": {\n    \"name\": \"authenton1 - CTAP2.1\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAbAAAAGxCAYAAAADEuOPAAAACXBIWXMAABcSAAAXEgFnn9JSAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDMgNzkuMTY0NTI3LCAyMDIwLzEwLzE1LTE3OjQ4OjMyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjIuMSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTExLTIwVDE0OjQwOjUwKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMy0wNC0xNlQxODoxOTo1OSswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMy0wNC0xNlQxODoxOTo1OSswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2NGRiZjU4ZC05OTY4LTg4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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAbAAAAGxCAYAAAADEuOPAAAACXBIWXMAABcSAAAXEgFnn9JSAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDMgNzkuMTY0NTI3LCAyMDIwLzEwLzE1LTE3OjQ4OjMyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjIuMSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTExLTIwVDE0OjQwOjUwKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMy0wNC0xNlQxODoxOTo1OSswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMy0wNC0xNlQxODoxOTo1OSswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2NGRiZjU4ZC05OTY4LTg4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\"\n  },\n  \"b50d5e0a-7f81-4959-9b12-f45407407503\": {\n    \"name\": \"IDPrime 3940 FIDO\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"8c97a730-3f7b-41a6-87d6-1e9b62bda6f0\": {\n    \"name\": \"FT-JCOS FIDO Fingerprint Card\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"99bf4610-ec26-4252-b31f-7380ccd59db5\": {\n    \"name\": \"ZTPass SmartAuth\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"a1f52be5-dfab-4364-b51c-2bd496b14a56\": {\n    \"name\": \"OCTATCO EzFinger2 FIDO2 AUTHENTICATOR\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"ba86dc56-635f-4141-aef6-00227b1b9af6\": {\n    \"name\": \"TruU Windows Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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AQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAD/vR07EAAAAAAQ5G89yIURAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAACAAQEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAAAgAEBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAIABAQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgIDPI8zSFbblcAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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AQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAAAABRAAAAAAAAFEAAAAABAAQQAAAAAUAABAAD/vR07EAAAAAAQ5G89yIURAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAACAAQEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAAAgAEBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgQAAAAADAgAAAAAGBAAAAAAMCAAAAAAIABAQAAAAADAgAAAAAGBAAAAAAMCAAAAAAYEAAAAAAwIAAAAABgIDPI8zSFbblcAAAAAElFTkSuQmCC\"\n  },\n  \"3e078ffd-4c54-4586-8baa-a77da113aec5\": {\n    \"name\": \"Hideez Key 3 FIDO2\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAAG0OVFdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMjFDOUI2OTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxMjFDOUI2QTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjEyMUM5QjY3NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjEyMUM5QjY4NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+vr5XIgAAE/9JREFUeNpiDDl6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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAAG0OVFdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMjFDOUI2OTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxMjFDOUI2QTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjEyMUM5QjY3NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjEyMUM5QjY4NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+vr5XIgAAE/9JREFUeNpiDDl6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\"\n  },\n  \"ec31b4cc-2acc-4b8e-9c01-bade00ccbe26\": {\n    \"name\": \"KeyXentic FIDO2 Secp256R1 FIDO2 CTAP2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"5d629218-d3a5-11ed-afa1-0242ac120002\": {\n    \"name\": \"Swissbit iShield Key Pro\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANEAAADMCAIAAABiENH9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAACZvSURBVHhe7Z0HdBzV1YDXDTeasSk2EEwxYMCAAQOGn0DoECdACKYkQCChBEiAEEgghIQEHAihdxLAXbLaFq16772veu99p/eZ1X/fzEpa1gWDZ6WV9t3zHR0dg3Zn3/vmvvtm572xjOHAMbWBncMx1XEg51RVHXUTNc1t9oKKt+LzH43Muzm8dH1Ew6lRnSttgysc7mNiiWOc1DFOEjPboaCvocdX2YdOi+q4OLL+1vDSxyNz347Piy2oqGvtcBME2OL15ttiv84JgtDW0ZFU5no3rfwRW8l1YaVrtpYf/XXtnG1tlmi3JU6yJI9ZUjGhBPQ49HvU6NxtrcdsrT17e8X1u0setxV/kF6eWl7b2dUliqLXngPGvp1jGMbV1BqWVfKcNffG8NIzwhqODu+cv6fPEjlsiSEsNsbi4CyxvI6ACQ307nawqPfBgYjhwyIGjg7rOHN33S1hJX+25UflltW3tHMc53Vo/7EP5wiCKK1v/iy99KGoovU7ylfsbJoT1ocki5eR5vCLjbZYSfTGmFAD+h2ci+WQCeCDnZ0b1nvczsZLdpQ9GlP0ZWZZZVMrRVFek/YT/s5BhgPhPkgqvCuiaM3u+kXhPXOiRixWGgkO2Jlx4bBzoQlpsVLIATDBUMJKzY0aWRLeszas7t49BZ+lFVU0tBw4233DOZ7na5vbPksvuWtP4Sk7XfPC+y1xem6zQ0al9np7TMgDVoB28RJIsiC877Qd1fdFFnyVWdbY1gHzAa9Ve8WkczDvaOvohBruoajCM8PrF0T2I5dhIIcXhZfGgylmb9BQC9pBcS9A8luwp/ec8NrHYgqic8s6u7o1TfO69c2YdM5NkMllrudici/cVroovBvJCzUjJFJsG+bAgCHgiVOwxIlLwjo3bC950ZabUVG3v8Ju0jlXS/t7aeU3hZUu395giRzSpwssrtswBweJMhQ4s2fg2G11m8JLPsmoaGjr2OfVk0nn7Pnlj1qLz9hVNy+8D2kLNRz89H9pDGY/wMQCTSnI+WG9Z++q/Z29JKGocnB42KuXT0w691Zc3vVhJcvCO9BfGjUcTnKYgwfVdrp2Nmb57rabdxe9n5BX09Lu1csnJp17NCL3zG3lh0X0o0oOCbvXi2IwBwacAXPixIXhvWu/Lv1ddF5KdZNXL5+YdO7m8NKjv671VnIwY/V7OQzmYHAw4NyciKFjvqq5LbIsvLLDq5dPTDq3PqJ+/s4O/c/0bxp8XwiDOUjs+vAa416wo/3y6IbPqwe8evnEpHOnRneiL+9tE9807PVyGMy3YtOrOhgno0bPtnW963J79fKJSedW2gb16yOcfrkFO4f5XhjyxHIWp7g6dujNOsarl09MOrfC4bYke9BVFr9XwWC+K04BXFrldG+p5716+cSkc8fEkugeKewc5tBxiuDSCXHE6w0HvCZ8jJNCzoGhfn+PwXxXDOfiqde+zTk9z2HnMIeON8+RrzXs4+4S7BwmAGDnMFMNdg4z1WDnLNEhj1+DBJrQdc5o7qh9ERlKwOedYu1muXOGWPvEVzK//xRNzJkghpg7e4FPZ3xM/3YLKLPWOcMeQyl0QrsnmVAtCrX7AiuxyEYstRNHOohlDnJ5LHWskzreSa2Mo1bFUyfGUyeNc3LC7OGUBPLkBHJVHHVMLLnERs6bypvTZpVz41lqUjj9FziP51qRWwt1t46KJVc4SfBpdQJ1ZjK9LpW+KJ2+PIO+Kou+Npu5IYe9JY/dlM/eVsD+rIC9s5D9eSF7l87molnC3UXsfcXo56Y89rIMGhRcbCe9jebXpIFg9jhnpC5IYxGQzNDvMHAsspFwHp8UT56ZTK1Po6/Mom/IZUCme4q5h8v4Jyr552qEv9QKr9YLbzQK7zQLH7SIH7dKn7VJ/2uXvu6QtnVK27ukHV3STp1ds4Xd3VJkjxTWLcHnfaKS25jJLIvVnYMG9GvVQDAbnDNO0PGsNi+GWGKHTEadlkivT2OuzUGJ6pFy9oUa/vUG4cNWEWSK6JGdA0rqkJI7qpYQaiWl1dJaE6O1sFo7p3VwWhen9fBar6D16fTPLgZFzS17hiRPmVv9uFW8vYA7Po6yROmFh1/bBoIZ7NyEanqJBpXZkXby5HjqglT62hxmcxH3ZCX/Sp3wfou4rVO090sZQ0qxW6ml1VYWyTQieSjFw6tjsmds3ystQyAGBc+ebvmXJdyJ4JxR7Po1ciCY2c7puQ0S2xEOcnUiDWMEFCt/rBbebRHDu2VIY2WE2sRqPWCY7KEVD6d6RM0ja2OqZ8zj/WQhHZDCw7ukX2Dnvh1vekO5DQqRtckUVGmPlMPQKW7vlNKGlGpKhZGRVsAtrNaBopPTdnZK9xZzMIHFzh0QPb0ttKHZ/tVZzOPlHAygzn6lilS7eY2QYcT0QCbD8a0BlesO7NyB8A6mbhhMVzjJ9ek0TPi3NAjWXrmGUqE+w0ntuwZ27tvQnVtgI06MJ6/PYZ538WHdcgWh9gsosXkPFMd3Cezc/jEyXBSxxIautN1RwL7RKKQMyZ28pmDZDiGwc/tHd26pjTw3hX6wlPusVSx2q6OSBwt3iIGd2xdgmw5kuHWp9MNl3LYOyUWp3MFuw43jQIGd2xd6hltsJ85JoUC47Z1SA63i6s2swM59k/EMd5iVWJNM3V/KbdWFk7Bw5gV27pvok4b5VuLkBOpnhewnbWItznBmB3bum+jOHRtH3pjLvNkkwKSBw8KZHdi5cYxRFco4G3lROv0nF582rMAs1XsoOMwL7Nw4unALrMSpiaiMi+iRe4T9bLKN49ACOzeOPqoujyVvyGXebharKFXGOS4wgZ0bJ4qYZyXOS6X/UM0lD8rDEs5xgYoeXtvdJd0X0s7pldzcaGK5k9yUz/6vXWxjNQV/dR+wGBS1iG4J3bMZH7LOeb9UJS5ASY7PGJbZKfx6C95J86DbOQF4WwDGdFnzAtl29tHMaFs7pbuLuJWh6xx85igCjua2fPazNqmZmaKrI6LqoWQPDOL9gtbNo/UQrawG/dHIaPW0WgtQao1ONaVWkZNUzkzgyF36B0kcULY0Cjfnscc69fUQgF+PBIJgc25uDHFeCgVJLnkwsNdHQGdGGRsQwS21jFAzh5X4AdnaK+/plnd3yds65a86pC/apU/bxI9bxY9axA9axPebxfeaxXd13tGBKc5MBI4cPg58ir/WCfeVcOvT6SMdobnWEF0icR9uJ6/PZqCbGxgNRrQABQzZUClmDSu7u6V3W8SXa4Wnq7hHyrkHSzmYxG0u5O4s5G4rYDcVsLfmMbfkMTflMjfmMjfkMNfnMNdlI67V+dHMBC3jzUWf4opM5pwU+vg48jDbXt0ROILFOf2a3LwY9E3XAyWcvU8eCUySg4oNXrmUUHd1SS+5hLuL2auzmXVp9BlJaHU7lDXHx6F1/CucFHzko4FY8igHCWkAOMKOgLNi1rDUTi60oS8Y5/h1R0AJFudQMYHWpV6UTr9Ui77pCsQ1ORAOKrb0YeXfjeIvirkN6fRJ8eQRDhIGdEN6xMTmMUZN7UvErAM+FLT81AypEwSLc+jDE8c5qR/nsR+3Ss2s+cMqCDcketIGlb/XCTBKQlZD+3RAcxu2Gb8Yv4cOE5/arzsCShA5F02cnkj/poyL6ZUHRPOznFv25I6or9UL1+UwK2JJ1OLoRPdper9DwgSIoHAO+jsSijnyojTmZZeQM6JQZl+Wg5G6hlI/bJE25bMnxlFz9XecthM9xAke5xbZSJhSwRy+llJFGAjNC9UzBonT2is/XMatTqTmw9sZI4vfYWCmhul3btyAZbHk7QXsri65mzf5LhJGQRPVNxvEH2Yxi23e2hHntmkjSJybE02cGE89WMo5+xXC7KskfYIW1SP/uow/PZHSc+qUz9Qwvky/c3qSW2Alz0qmf1/FZ4+ogqlrujyesTpKfbtZvD6HRU+QgreDSs7vGDBTSZA4t8ROXpxO/6VWgEHQ1FpuDErD3BHlD9X8eak0lIzG2/kfA2YqmX7nIOtEE0c5yKuymH81Ci7K5JWrbtnj6Jfv12/XQZs1g3B4YJ1egsS5FU7yplzm/RahiTHTOUiZXby2rVPalMeC1pPXR/yOATOVTLNzqKJHP1fq9y990Sa2c2bOWSVtrI5WP2gRr85iFqIZKxYuCAgS506OJzcXsZCQugUznWPVMSgQtzSKl2bQaDt67FwwEATOoSsXqxMoKLnCu+U+U7/1IhVPzojycq1wYRqN3g5fJQkGgsS50xOph0q5qB550FTnRmVP2pDyxxr+3BQKvR12LhiYfucikAdnJFGPlHPWPnnIVOeGJU/ioPx0FXd28vjVYL8DwEw9QeLcmiTq0XLOZrZz8GpxA/KTldyZ2LngIXice6wc3R5srnMwUjv75ScquDXYueBh1uc5cO5J7FxQESRzCKjnjLs1zZ1DDIue+AH5d3hsDSqCxLnT9HlrZI/JdwiPSJ6UQfRl61qYt2LngoQgcW51AvXLEi6sW+4VzHSOkD1Zw8qLLv781PHrc77vjpkWgsS5k+OpzUXctk6pizfzewhG8RS71X/WCxvSGfw9RLAQJM6h71sL2C/axTZTd0QXVU8trb7bLF6VxaBlw+AcvpFp2gkC59DPFU60h6vp95WonrFOXtvaMX5fiXHzHE5100uQOHe0fv/cGwG4fw5Kutg++UH9cZHe5V7Yuellmp0D9Nyz1IbuE/5rnfn3CUvaWOGo+qJLuCiNWYLvEw4GgsS5BVby7GTqmWo+Z0QBS8yNVlb7b7t0RwF7AtpNkrBE4FQ3rUy/c9D9+rqvHySQD5dxCQOy6Quq3bInY1h+ycXD7BUtiQDncFU3jQSJc8DyWPLOQja8R+oTTV7fKmsemElE9kiPlHNnJdOL4U1RtjN2vhn3z8D3wDABYvqdA6CzI4mldrRR+idtYiOrSqbuIQyvJWroosn/OtDeuecmU/Behuhe5wwmzMPyBZTgcW6+lbw0g/5Hg1BMKGwANnWlFU8lqX7VIT1ZyV2dzZyeSB3npGDuMt84gCg953kxUuCBmfifTWJC/e/KxHli4NuwwUnQOOeGkg6mEU9V8vED8ojp8wg92zGKp5HR4gbkt5rFx8r5W/PYi9OY05B85JEOYqENPSMA7f5ndJ5f184IZoR2weMcNNmqOOrnhezXHVKnqau/fAPmJ0OSVkGq9j75wxbpLy7hsQpucxHaw/WH2cylGcyFacw5KdSaZGp1Itp588R4amUcdXwcCV4eq7PCDKB4BY7RWaZztANxlA7a01Pf1hPthmlDu2EuAWzEYhuxyEYstKJnPS6wEpCh58Xs6ySZyMTBaWFQOAfojQXNfWUm83qDAINg4LZMhxfmVM+g4GlitBK3mjIow/QCxtz3W8QtDcLLtcJz1fyTFdyvyzgo/u4u9u4t/JN89sd57C157M25iJty0SbD35sbc5nrgRzm2hzvHr9XZzE/zGKuyqSvzKQ3ZtKXZ9JQaVyaTl+SRl+URq9Poy9Io85Ppc5NodYmU2clUWckUacmUj9IoFbp+9GCwWDqIjuy0OvZxHiNnds30C5RcPqS0JS/reDAA0bxvnVAA8yG2nFIRPvz19FoA/XcETV1SHH2y9G9Uli3tL0L6ahvoC591Cp92Cp90AJ2Su+1iIfCu/r+5f9pEt9qEv/dKL7ZKP6rUdzSKML59s8G4dUG4e/1wit1wl9rBcjEL7qEP7mEF2r4P1bzz1bxv6+CU4J/vIL/TTn3EJwYpdzdRdwdaLtt9ppsZkM6ytM/SEQWLraR3q0Lgkq+IHIuGl2lg1Hmp/looWsnrymBGmD3ESCfqKH91EnZMyqBhZ5+QevhtS5ea9cfF9HCaJAXoRxs0Kk/NOpohItC1FBatU4VpVUSSgWhlBNKGaGUEkqJWyl2K4WjSsGokj+q5I0q2SNK5rCSPqTAiZE0KEPt60CnhwynB9QkH7WCteLz1fwDpdyNOcy6VBpSILq5YUI7v2afFoLFOQC0i0RlyoZ0+tU6AVp5alJdUIXHg9D0n8YvcDIYD+UBZP3EEAAVygN0hsBknFTQSTIgoNOjSX/WBRgZ3i1DKn22mr+zkIUx+uR4VBRO5rzpTXhB5BwAJUg0cUoC2oguvFvqMXVN/6wPaCxZ84CIoGA3r9VSWsaQsqtLghP43mKYodMwd0E7xE/MLXxbfioJOudgJmGHmQT9j3oeBpfAzSRCIXjV0yd4ikbVHZ0SlIMwAVqThHaL9ya86dIuuJzTM/989N0rdX8JC2UK1FWmfiURikHLHihJYcD9oEV8qJS7MI3+xq2EU29ecDlnNAF6tCF5RQb9Wr0AFTRj9lf+oRlgXi2tQsUC094rMpnlsRQaZw3zfLtgCggu5wwiiTlRaIXEPUXs1k6pPQDPJwnNgPIYGtPep/y5Rrgqa/whGVOf7YLROb0hINVdkEo/U8UnD8im72odsgET4V7BkzQgv+QS/i+LWeaYjtouGJ3TTztoi6NjyetymP80iZWkyuHZhEkB7TggaHH98nM1/IZ0+oiJ2s6vFwJHMDpnoLfC6gTqnmLuqw6pkdawdWYFTMs6Oc3ah3ZyOTeFWmDFec5AP/kWW8lzUujHKjiYw/aJHlzZmRWyNtbKaXAy/7yQPSmemg/aTdkIG7zO6SMsNMThdnJDBv18jZA0qPSL+MqJacGraMH5lgbx2mzG++SMqRlhg9c5AJzTrxIviyWh4H25TkgdUoYDcGtdyMaw5EkYUH5fya9NphfE6NpNQaoLaucAPdXBfGKFk7w2h3m1ns8YkofMvXU9hEPxjDWz2udt0o/z2BVOfRuhKdAu2J0DjIZATxQmr82m/1bHJw8qfYLH3GWwIRus6skYVp6t4tel0gttxiM0AryT0AxwDtCdmxtDHOtEz9t8ySXAVB9mXpLJa/5DMeDMbWS0T9qkn+azx8VRqKkjsXPAeKqDUxAG2SsyGDgvw7slF6XSgXhuf4iFW/LA0PFMFb82hda318DOGUBb6OZBtjvKTq5Pox4uZT9rE4tG1SF8DeXQAoaLOkp7r1m8JptdYqzCNFrbrwvMYsY4Z6BrByfiYhtxZhJ1RyHzz3rB2qu4KA3MwzcDfM/wjI2Inuhe+d5ibmU8Nc8QDjs3CbSFfmvnQitxYjx5RSbzSBn/QYsEowNMwQgZzy2+T4jaWI7+xFGYSSwO9E5CM885wDgL0RDgXmonz0qib81jn60WPm0T4/qVckJt57RhEd0xizPfQQa0Uy2t/qdJvC4n8NeHZ6RzgOEc0g4SHlp2em4KfWMu85syDkbbrR1S4oBSSqgtrDYgeCjFI6joWhS6rIct3E9089rOLum+YvRVGGrYwM0kZqpzExjmRaGlxUc5yDMSqf/LZDYXsk9X8a81iJ+2SWHdsrNfTh9C66ZKCaWKVGoptHyrlUPrC6GhewWtX/AMiJ5B0QPZcURC6wlgKgfDNEDqUDq0vubFF+b7AjkY4FQEr6I1NTC6SRpaZaPqS2+mPuCDJw0q0G5nJ9PoBifs3H6ZSHhANFrgDuatiiPPSqY2ZDDX5bJ3FHAPlHJPVPDP1/Cv1AlbGoS3m8QPW8Uv2tHiPDiz9/RIUD7b+uTYfjmhX04akFMG0WK+zGElWyd3WMkfUYGCUUShG1H0fSkm1BJChRxcRqhQBlQSahWpuigVhrYGGiVmOBN6eG1Q1NyyxqqeKbsWxCpjcGD/bBA2ZNCB/cp/xjvny4R8envNs6K9nlbEUifHUzDJPT+NujSDviqLvj6HuTWPua2AvauQva8YGfnrMu7RcvCS+10l90wV91w1/3w1/2cX/6KLf8nFv+ziX6kV/gbUIV6tn+Qf3x3oVGjr1xuEfzUIbzQKbzYKbzWJ7zSjhdYftYqft0tftkvbO6WIHsnWDxMjGdIzJOZeXoPsGND8B1kW0v/HbRKUdIvt2LmDZCLnod060AYo6F+i0SU9yH+LbcQRdrQnCNTIUP8d70Tp8KQEtP3C6kTqtERqTRIF2XFtMnVOCnVeCrUuFe3VcEEqdWEqtT6NuQhIR1ysc8n3BbIvcGkGc1kGc3kmszGTgak31AM/zGZ+lMPckMvcnMv8JJ+5E86HEhbOBEjPUNpH9sglbnVA1AI3K9JvIdZ2dcu3F7DLYBphNGYgtJtVzvmhC+e18CAx/sRAfxEoEyexIuYeGpB9ARi8fIFTYqGNWGQnljqIwx1QHhDLnST0yqmJ1Pkp9HXZzEOl3L8aRShMmxgtcLdMQ/3q6JcfKGFPjAvkPeuz2TlgQiBfsSINjM2LDpKJvzIV3+PxHh6kZwN02PNj0F2ry2ORfJAIYejf0SXV0SoXmHQHUxkoZKHGOCOJ8t7aBPg16aEzy537fkyYOl14/dMZ1xFq03Up1MNl3K4uqYVRAzG3ULSxglHlhRr0qCpw3XsAfo1z6GDngot9ygfaod2PiSVWYl0KDbOc+H4ZajvV7NsI4eXKSRXmSZelM1D7YudCG8O/CHSn/jVZzL8aA7XavIZSYTZ9tb4METsX8oB2e5AEqxOoB0q4Pd1Sr6nP4zOigUY3mNyYM77iGlKs32EcOti5mYRe2x2lP6dqS6NQTZr8zCCIFkb7tFX8SR5zAvQ4dg6DJIhCk1mYVz5ZycEc0/TrJh2c9mW7dGcBc2Lg7hnGzs0kjKouGi0N2VzE6o/1Nvn5Ld28tr1TuqeIPTlw3/Rj52YY+ni31E7elMt80S61sKps6uy1V9DCuuVflnCnJIw7B6L7HcMhgp2bYYABkWiz76symbebxCpS5U0dXvsELaJHfrCUg5kKdg6jg5xzz40hNqTR/6gTCkcV2tQrJv2iFtUrP1TGnWo4F4Gdw+jOzYkhLkilX3IJWcMKYeo3ElAgRvfKD5dxpyVi5zAGunPw89wU6rlqPnVQHjV1c75J55L0Zf3YOcyEc2uTqWeq+CT0bDRTx1ZhfGw1nMP1HGbCg7OTqd9X8on98rDZzkX2yL+COQSMrdg5DMLwIIZYm0I9XcUnmp3nYN66p1t+AF8rwUwy7hzUc3+o5lPMrud6eW1Xl3xfMfcD7BzGi+4czFvPT6P/7BIyzJ63dnHatk5pc1EgVxxi52YYunNzY4iL0+lX6oS8UcXcXYLaWe2/7dLthexK/H0rxgtyjphvJTdmMW80ieWkyfepNzPaR63irfnMcdDjyLm9DuDQwc7NMHTnFtnJa3OZj1vFBkaVTL2fqY7W3m4Sr8tlluP75zAIEA480DdYhuFvZ5fUw2tmKucZqyLV1xuEK7OYyWeC+R3DoYOdm0nozs2JJk5OIB8qY+MHZNLUYg70LXGrf3EJF6cxSwO3OxN2biahS7DYRl6YCpNWvtCtKKYOrJK+I9gzVfw5yfRCY/sI7FxIo1dyIAFU95vy2E9axWbW5P2UaWUscVD5TRm/OoGaPz6O+x/GoYOdmxkYBkSgfTDOTaGfruJgYDX3URmQMIdET1SvvLmYA63nGMLB+/odyaGDnQteoL8NoO8hw+mPGD3OSd1RwP63XWygNcHUgVX1jLVz2pcd0i357OSD57BzoYVhmyFcBPplmQM9D+jVeiFvBN2qae5KCDC4mlLfbhbhLQ6z6W8aCOGAoHNu4szGGEQT86KJxVZiZTx1TTYDU4eEAblPMH9lK0yBs0eUl1zC+Wn0HJhAhIpzhm2RvpvTzAoi9sL3P038DvioBiywov3LToonL0qjf17EQiclDsjdvCabrxx6qKu1V36kXL9bE/oCnPPtGhMJKufmWgnI6ovsxBI7OauwTbLUruMgD3eQUDYd6SCPiiWXxZLL48hj48gT4qkT49F+eGuSqHNTqEvS6R9lM/cUsy+4eKi0CtzKINqmxNsp5kYLo33WJv20gD0uTt98LhScg3x+pIM4JZG6II2+LJO+QufyWcHGDMQVmcyVOsYOhzBQXmdscpjHbMpnbi9k7ypif1HC/aqUe7yCf7oK7fK5pUH4qFUM75Eyh5VGViMDtuMheFxGqH+vEy7PYA53GKONfweZRrA4p2/FekoiWrb5RCX3ch1v7IQKrTALeFUHbexajzZ2BZPeaBT+3SS80yy+3yp+3Cp+3iZCGtvZJUX0yLZeOWFAyRhWikYVF4WeOzAkeTgVaRG4QFfmBpRHyvjTE+nDAnc12CAonNPLOKhdLkyj4fze1S1BMVvsRhSOKgUzn0K3UoRQi93eDazLCbWCRLtX14zvXt3MaqBXF6/B/GBIRFu2c6pHmZKHhkL2hPf9ulPalM8eHRvIHTYNgsU5fZ0wzNL/3SRCkoc5lKTvXQ+IswvjQ8EkwAD62wDSmKYDlk2FaD5BKx44JV6tFy7JoNGz+QMqHBBUzkGJA+VLCxuAWRmO/UcvWrsvQR0JxbQx5vh3kLkElXM/zGLeaxHraFU29Qo7jgMEDN/lpAIl5tVZzNGBu3/Jl6By7qosBspqKHHM3YMDx/4CWnlE0qx9KMmtTqAWQF9g53AENFgVkpz6ZqNwVRa9dOI5JIBfB5kLdi6Uo5NDKwu9O3/pvYCdwxHAIGUtY0h5oUa4JJ05PHC7pO8Ndi40Q1DRXSQwY7sxlznWSaLH8BsDq1/XBALsXAiGonmaGG17l3R/CXdqIjUvZvzZaH79EiCwc6EWigdtGhzVI/+2gj8vldYfYag759cpgQM7F1KhjY318J64fvm5an5DBsxV9VtIDPw6JXBg50InJM3TyWuOfhnmDVdk0gF80s2Bwc6FSIjaWAurRvdKz1bzGzOZZbHj8wbsHHbO9IACzi15Kgh1W6f0ZAV3STo9+R0XtDzg1x2BBjs3u0PW0IXftCHlP03iPcXsulTqCIe3wafBNgPs3KwMaD9G8cD8tMSt7uqS/+QSbsplTkmgFtqmL71NgJ2bfSGoyLZitxreJb1aL9xTzF2cTh8XR86Hpp4YUn3bf4oJKueuzmI+aEH7W2lTcn/s7Ag4PSUNfVtPyJ5eQaun1ZwRZXeXBD36QAm3MZNeFU+i9AaNDLb5tfy0EFTOXZPFfNgiNjEmb8Mx+0LT97OB0XNU8vTwWiOjlRBq8qC8u1t6u1l4robfXMRemcmclkgd6dDvNZ9YxejX8tNCUDm3MYN5rV6A07RP0NyyBxiWZjUiYkhnUGdAQPTp9Argk6eL98AkoJ3TWlmtiUVpzEWp5SRaZpE+JDv75bBu6dM2cUuj8Ew1f28xe002vTaFPs4Jpdv49d5pH0z9CArnAP2xpOekUL8u4z5oFaN65dh+hK1vNmPtRcT0ytG9clQPIqJb2tMtQbra1SXt6JS2dkpfdUj/bRc/axM/ahHfaxFh+vl6g/hKHf98Df9UJfdQGXdXEXtzHntlFnNeKg2zhGWxpHfhFsxMjfRmmOfX4NNIsDgXTcyLIeA4Ls9k7ixiHy7jHi1HgIKzGPiYBqDOr0q5B0u5+0vYX5awkK7uLmLvKmR/VsDeVsD+JJ+9JY+9MYe9Npu9KpO5LINZn0avTaZh6FyVQEHXHO5AFdtcvRm9BKFqEwSLczHEnBhisZ04No48JZE6I4k6MxmxJmk2Ax/T4HQgkQKHTk1Ei/ghXf0ggTpJX9O/Mo6C7jnOSa7Ql/sf5SAPt5OLbGhYgBbz6mWkNC/BN5j6ETzOGUA7AnDKhhTGp0ZEfwNvrtonIJaB378De7VqcBFczkF7obNWP1/99pIJafRMNiHZBDNFMj+CLc9NtiPmwPi12wwi6JzDzHqwc5ipBjuHmWqwc5ip5qCdo7BzGHMwnIunXmsQvXr5hI9zsaQlZcwSy/v/PQbzXQHnUiDPEa8f2LkVDrcl2YOdw5gAjJZJnlXO0S31vFcvn5h0bqV1AOnp4CxWymIl/V8FgzkYwBzwJ5YD7VY7Bt+spb16+cSkc6dFdViiRi02BgF/5vdaGMzBYKMsNtpipy2RI2utne/WjHr18olJ5y7aUzd/Rzv6M0h18Ge+L4TBHCRgm4O1RLsP29G2Mar+i+p+r14+MencreEly752zYkatsRJFjvj/1oYzMHgYCxx4pyIwRVf19wRWRpR1eHVyycmnfttZO7Z28oWRvRb4iWkKi7pMN8VcAbMiRMXhfeet7X0mZi8tOpmr14+MencO/F5N4aVHBPWgZIc/CUaXrF2mIMGhANndHNW7G79cVjRR4n5rpZ2r14+Memcs6Dyt7aSs3bXzQ/v9QqLZxKYg8cQLoZcsLvn3N2uZxwlySVVQ8MjXr18YtK5utaOD9PLbw0rWbGtzhIxaImX0WQCpzrMQUGiK7tQlYX3H7+t9rbw4i+yKps7uiRJ8urlE5POESSZVl77J2vuJTtKl4R3w6iMXgVfq8N8K2AIeOIULHHC4WEdG3eW/NWem1NVT9P7uDgHMemcqqodnV2ROaWPxhSuDas9LKIPZUsnmKdfOsHmYfbGqOH0K8AWG7VwT8+6sJqnbIX2/PKenl7PfpbNTzoHIQhCQ2v7l5ll90YUnLajekF4H7pugi6dQGGIazvMXoAV4AYY4hQXhveu2V75YFTB9uyylo4uUdzHN61GfMM5CJZlKxpaPk0pui+ycO3uuqXh3XMjh1HmhNoOQPJBzgP/cNoLSYxh1JguGEpYqXlRw4eHda0Lq30wsvDLjOKa5jZIXl6f9hX+zkFQFFXZ2PpVZvmj0YUbdpQev7NxblgvuoACFSIYDW+Dh9qQBQ2mDBpMjQHQxszb3b1yR8PGnaVPWgu3Z5e7WtoZhvGatJ/Yh3MQHMc1tLRH55a9aM/btKf07LC65WHth4X3WvYMWqLd3neFIRyqPUwIIegZhwEH5kQMwmC6fHfbObtrb9sDk4Y8qOGa2zsPnOGM2LdzEDAed3V3p1fWfZxZ8ZSj5Oaw4rVbS5d/7Zq7tQXdCgBHkDyG7vHEhA7Q49DvkcPzt7Ucu9V13rayW8OKn3aUfJZZkVVV39PTs88rI3vHfp2D0DSNJMn61vb4oor3Ewueis77aUTZZVENZ1u7TnEMrnK6T3ASJ+hr0DGzHQr6Gnp8tWNwrbVzY1T97RGlT8fkf5iUn1Rc2dTeCfUY2OL15tviQM4ZIcvy4OBgdVNrSnVzWGX759UD77rcb9YxW+r514AG9ChwzOynnoce/3cd827N6BfV/Xuq2tNqml3NrUNDQ4qieF05uPh253DgMDewczimNsbG/h+9P7+KfKO+RgAAAABJRU5ErkJggg==\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"d41f5a69-b817-4144-a13c-9ebd6d9254d6\": {\n    \"name\": \"ATKey.Card CTAP2.0\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAU0SURBVGhD7Vpbc9NGFD62bMWOLzi2kwJ2LpAWSgt0IEBvT33tdKYz7Vt/YB86w2/gpZ02hYdOAk+FaSBpIDeH2CW+yz3fareR09iyoks8jr+ZM9autOv99O3Zc7RS6KeVtQ6dI4Tl77nBmPCoY0x41DEmPOoYKA7Xmm0yaLjDdZhCFItqstQbtoRB9vubc6RHwtQZUs6hEFGjZdDDp69sSdtOaSiraxpFwmGKasNpGJvOv4PMwoF8uDOs0low6Bhtp/Rhs0U/3L5CUZ7SwPPdCm2/q5KGeXSGaDPBmUSc3s+nRLnZatOPK2s0GY2Ici84Jvzryx36c6/C0+hsCbeMDn2QS9Hn89OiPChhx2EpzMqC7Em+FKRhDBiLUzgm7BYGT8U2qwPDcdAIlDBIxiIapSeiwnCMuiARGGGsom3DoG8/mqWvPywIwzHqgowCgRFuspK3Lk7J0hFQh3NBIRDCULDFSt6+9H/CqMO5oFQOhDAU/HgmI0tE7xotqnK4U8A5hJkg4DthKGewgveKOVlDtPJ6n/7Y3JcloqVCNjCVfScM5a7l07JkhqXnpbIwRTDE8fT6dDoQlX0lrHx3yaLuKqsbDoWFrbw5Uvnu5VwgKvtKGDF2kdM/PM0orG69pSgyNbbVN29lLYnsCdf6HZd9Iwyl6u02PSjmZQ3Rs619fkw3p7AwWadwv5ATbfxU2TfCeJpZyCSFcgpP/i6RxmobrCIMx082SvIskc6ZF9qgrV/whTAUarQN+mzOfJIBXuyVKaVHKMmWkIbj1ESEz1XkVUQPZnOirV8q+0IYCs2mJ7u2WxZzafru5jx9c6PYZaiD7ypM6lEqclu/VPacMJRpskLWldkpltiX0YcfKntOGItsgRW6ENNljXNk4rrow48F2/GOx/KrXXpRqnQtRlYgrOC53BSn0xWS6qzaV1feo8sXJkV58+CQHv21RROWvhCLeVj/9aH12FnBDFjMpujTOTMK+Lbj0Q/IouLst1enkrQwlRAZFkjCH4UJyaz3V24GyPO4Fm3QFn2gL683CTwjDH+r8V3+cn6a7s/mxQo9l0mIemzFmIYrrYqZdeo8rkUbtEUfX/Av+vTSlz0jDPGy7Hv5REzWEP28tt1z6p+EKE//X17uyBLRdDIm+vTSlz0hjPE0OENCPqyw/U+VyvVWl552gN8e1BrctiZriO5cyrK/ssqy7BbeEOYpl+L4WZCLEbC8vifeBiCFHBS4Fm85Hm/syhqiIk/xJPft1bT2hDDe69zlZ1qF0mGdStW69FlnQJtdtGdTuMN9I/vyAq4JYxXVtRDN86qq8Nv6DocazZG6CmiDtsvrRyovcN/i3ZEHKrsmjDuPFVWhLHyw3jN+DgK03WI/Rl8K9zxS2RVh3HGocZUTAAUoE5NJihtMcB+/b+zJkpmLI0Fxq7KrkSHb+cSyE4nNudeVqoipboGXdZvlQ9Gnwq2LGfGfbnBqwlg1xS5FNkl1Tg7wfLvMvou6fr5rjcv9YjT6wPnHFl++MZMRbyvcqOwqlwbpGq/QZiQ2CVhz5+PAQOM84Igk2mK1qnyzes0I9I82aX4QwTGuwxcJTc63seEXeC4NFZDvxvlPYP3IAhgwCJZrTWH9yALoH+dxbYWTmAP+Bdl+M8gOrgifBiCAVRjWj6wCyKnrYW7IAo4JY4phOmHxOEvDGE7jy+NPHo7jOOFhhaeLllu/CQKDjtGWML5ww6Mftl5O8qVhMIwNaSfGagfbKQ2cq08PRw3DvRL5gDHhUceY8KhjTHi0QfQv3WxwqZwG02wAAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAU0SURBVGhD7Vpbc9NGFD62bMWOLzi2kwJ2LpAWSgt0IEBvT33tdKYz7Vt/YB86w2/gpZ02hYdOAk+FaSBpIDeH2CW+yz3fareR09iyoks8jr+ZM9autOv99O3Zc7RS6KeVtQ6dI4Tl77nBmPCoY0x41DEmPOoYKA7Xmm0yaLjDdZhCFItqstQbtoRB9vubc6RHwtQZUs6hEFGjZdDDp69sSdtOaSiraxpFwmGKasNpGJvOv4PMwoF8uDOs0low6Bhtp/Rhs0U/3L5CUZ7SwPPdCm2/q5KGeXSGaDPBmUSc3s+nRLnZatOPK2s0GY2Ici84Jvzryx36c6/C0+hsCbeMDn2QS9Hn89OiPChhx2EpzMqC7Em+FKRhDBiLUzgm7BYGT8U2qwPDcdAIlDBIxiIapSeiwnCMuiARGGGsom3DoG8/mqWvPywIwzHqgowCgRFuspK3Lk7J0hFQh3NBIRDCULDFSt6+9H/CqMO5oFQOhDAU/HgmI0tE7xotqnK4U8A5hJkg4DthKGewgveKOVlDtPJ6n/7Y3JcloqVCNjCVfScM5a7l07JkhqXnpbIwRTDE8fT6dDoQlX0lrHx3yaLuKqsbDoWFrbw5Uvnu5VwgKvtKGDF2kdM/PM0orG69pSgyNbbVN29lLYnsCdf6HZd9Iwyl6u02PSjmZQ3Rs619fkw3p7AwWadwv5ATbfxU2TfCeJpZyCSFcgpP/i6RxmobrCIMx082SvIskc6ZF9qgrV/whTAUarQN+mzOfJIBXuyVKaVHKMmWkIbj1ESEz1XkVUQPZnOirV8q+0IYCs2mJ7u2WxZzafru5jx9c6PYZaiD7ypM6lEqclu/VPacMJRpskLWldkpltiX0YcfKntOGItsgRW6ENNljXNk4rrow48F2/GOx/KrXXpRqnQtRlYgrOC53BSn0xWS6qzaV1feo8sXJkV58+CQHv21RROWvhCLeVj/9aH12FnBDFjMpujTOTMK+Lbj0Q/IouLst1enkrQwlRAZFkjCH4UJyaz3V24GyPO4Fm3QFn2gL683CTwjDH+r8V3+cn6a7s/mxQo9l0mIemzFmIYrrYqZdeo8rkUbtEUfX/Av+vTSlz0jDPGy7Hv5REzWEP28tt1z6p+EKE//X17uyBLRdDIm+vTSlz0hjPE0OENCPqyw/U+VyvVWl552gN8e1BrctiZriO5cyrK/ssqy7BbeEOYpl+L4WZCLEbC8vifeBiCFHBS4Fm85Hm/syhqiIk/xJPft1bT2hDDe69zlZ1qF0mGdStW69FlnQJtdtGdTuMN9I/vyAq4JYxXVtRDN86qq8Nv6DocazZG6CmiDtsvrRyovcN/i3ZEHKrsmjDuPFVWhLHyw3jN+DgK03WI/Rl8K9zxS2RVh3HGocZUTAAUoE5NJihtMcB+/b+zJkpmLI0Fxq7KrkSHb+cSyE4nNudeVqoipboGXdZvlQ9Gnwq2LGfGfbnBqwlg1xS5FNkl1Tg7wfLvMvou6fr5rjcv9YjT6wPnHFl++MZMRbyvcqOwqlwbpGq/QZiQ2CVhz5+PAQOM84Igk2mK1qnyzes0I9I82aX4QwTGuwxcJTc63seEXeC4NFZDvxvlPYP3IAhgwCJZrTWH9yALoH+dxbYWTmAP+Bdl+M8gOrgifBiCAVRjWj6wCyKnrYW7IAo4JY4phOmHxOEvDGE7jy+NPHo7jOOFhhaeLllu/CQKDjtGWML5ww6Mftl5O8qVhMIwNaSfGagfbKQ2cq08PRw3DvRL5gDHhUceY8KhjTHi0QfQv3WxwqZwG02wAAAAASUVORK5CYII=\"\n  },\n  \"e86addcd-7711-47e5-b42a-c18257b0bf61\": {\n    \"name\": \"IDCore 3121 Fido\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"95442b2e-f15e-4def-b270-efb106facb4e\": {\n    \"name\": \"eWBM eFA310 FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAExCAYAAADvDYgqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAFicSURBVHhe7d0HeBXF2sDxN73QCTVA6FIFFKkCUuyAEumKYkFUbICCIiKCUgQE7L0gdlQsKCpSrIggSC+hJnRCJ4H0b2fveD/0khCSnc2ek//vuXmYd46XkJNz9sy7M/NOQJZFAAAAAABAgQrUfwIAAAAAgAJEgg4AAAAAgAeQoAMAAAAA4AEk6AAAAAAAeAAJOgAAAAAAHkCCDgAAAACAB5CgAwAAAADgASToAAAAAAB4AAk6AAAAAAAeQIIOAAAAAIAHkKADAAAAAOABJOgAAAAAAHgACToAAAAAAB5Agg4AAAAAgAeQoAMAAAAA4AEk6AAAAAAAeEBAlkW3PSszNVXSDyTKqa1b5dSadZK6e4+kHz9m94n3//mAcQEhoRJcupQER0VJWJVKEt6gvoRXryZBpUpJQCD34QAAAABf4NkEPSsjQ05t3iKHPvpEjv+wQNL37ZOs1DT9KICzCYyMlNAa1aTENZ2lZJfOElqhvPWOD9CPAgAAAPAazyXoKjE/Mvc7SXxzhpxasVL3AsiPgNAQKdqxvZS9Y4AUadJY9wIAAADwEk8l6Md/+132jHtKUtat1z0AnFa869VSYdgQCatSRfcAAAAA8AJPJOgZJ07InolT5PAHH4tkZupeAKYEhIdLhVEjJKpXdwkIDta9AAAAAApSgSfop7ZslR0DB0nq1u26B4ArAgKk2BWXSpXJEySoaFHdCQAAAKCgFGiCfuLP5RI/YJBkHDmiewC4LbxRQ6n25isSEhWlewAAAAAUhAJL0E8sXSY7brlDMpOSdA+AghJaq4bU/OhdCS5dWvcAAAAAcFuBHJCslrXH33kvyTngEambt8r2gYMkg/ckAAAAUGBcT9DTjx6T7bfdIRmHDuseAF5w8s+/ZOcjj0kWhRoBAACAAuHqEnd1xnn8Aw/JsS/m6J5zFxgZIUGlSklIjeoSVKK47gUKOettnL5vv6TtiJeMI0clKy1NP3DuKk4YK2X69NIRAAAAALe4mqAfXbjILgp3zkepBQZK+PkNJKp/PynWuqUElykjAUFB+kEAf8tMTZXUXbvk6Lfz5NDM9yV9z179SO4Fliwh5/3wDUXjAAAAAJe5lqBnJCVL3FXXSFrCTt2TO6E1q0vFUSOkeNs2dqIOIHcyT56Ugx98LPufeV4yjx3XvblToltXiZk6ybpCBOgeAAAAAKa5lvEemfP1uSXnVmJQomes1J4zW4pf0o7kHDhHgRERUvbW/lLLeg+po9TOxbFvv5dT8Qk6AgAAAOAGV7Jetex2//Mv6SgXrGQ8atBAiXlqvASGh+tOAHkRVqWyfYRa5MUtdc/ZZZ1Kkf3PvqAjAAAAAG5wJUE/sXiJpO/ao6OzCAiQ0jf3k+ih97O8FnCIutFV7dUXz2km/cSCRZJ+5KiOAAAAAJjmyh50Vbn96Gdf6ChnKoGo+fH7EhgWqnvyyfrxstLTJf3ECck4flyyUvNe3RpwizqtILhYMXuZul0Q0aGbVae275DNV14jWSkpuidnlZ6ZIqWv6aIjAAAAACYZT9BVcryueRvJPHxE9+QgOFiqz3pPijZprDvyLnndejk2f6Ek/bpYUrZslYzEg/oRwHcEx1SRiNq1pGiHdlKsY3sJq1hRP5J3+156VfZPmqqjnBW9rKNUf/VFHQEAAAAwyXiCnrxmrWzp2l1HOSva8RKp/vrLeZ4tzDyVIke+/U4SX3tTUtZt0L2AnwgMlKKd2kuZW/tLsRbN8/w+ST96VDZ1vFIyDh3WPdkLKl5c6v7xswSGhekeAAAAAKYY34Oe/NdK3Tq70n165S3pyMqS44t/l7jO3WTXkOEk5/BPmZlyYt4C2X7DLbJt4CBJyWOV9eASJaTEtV11lDN1VFvqzl06AgAAAGCS8QT95PrcJcsBkRFS7JK2Oso9VSF+98TJsuOmAZK6dZvuBfyYStR/WCibu14nh+d8Y8fnqkSXq3QrZ1lpaZLC+woAAABwhdkEPStL0rZu10HOwhvUl8DQcysMp4q+bb/jHjn46pv2XnegMMk8dlx2Dh4me6Y+Y7/XzkVEzRoSWLSojnKWsieXJzAAAAAAyBejCbra3p6RnKyjnKmzms+FSs633TpQkhb9pHuAQigjQxJfeMVeRXIuSXpARIQEl43SUc7Sd5OgAwAAAG4wO4OemSmZuTzOKahCed06O7XsNn7YCDm5bIXuAQo3tYrkwFszdHR26ui2gNDcFX7L2LdftwAAAACYZHwPugn7X3tTTnz3g44AKPsmTZOkFX/pCAAAAICvMXrMmtoXvqlLrKRujNM92YsaNFCihw3VUfZOboqTLV2us2fRcy0w0N5vG1wmSgKjSulOwKOsd2TGzl2SceKEZJ5I0p25E1qrhtT+8lMJjIjQPWeWlZEhcZ1jJWXjJt2TvZLdukqVaZN1BAAAAMAU30rQrX/qttvukBMLc7nv3D43uoOUHXCzhNerK8HFiukHAI/LzJS0w4flxO9/yIHnX7ISaes9lJu3akCAlB8xTMrdfqvuODMSdAAAAMB7fGqJe9Kq1blOzkNiqkj1j2ZK9VdfkKLNm5Gcw7cEBkpIVJSU6nyV1J4zWyo+OVoCwsP1gzmwkvjEl1+TjKRzm3kHAAAAUPB8J0G3Eo8Dr76hg5yFn99Aas7+SIpe1FT3AL5LFXQrc30f+4ZTUMkSujd7GYcOyxF1PjoAAAAAn+IzCXr6kaOS9MtvOspecMXyUu31lyWkdGndA/iHIo3Ol8rPTbVe5EG6J3tHPvtCtwAAAAD4Cp9J0JNWrpLMY8d1lI3AQKk4drSElCurOwD/UrzNxVLqhj46yl7ynysk4/gJHQEAAADwBb6ToP/2u25lL7xeHSnR4RIdAf6p7IBbJSA4WEfZyMiQpJUrdQAAAADAF/hMgp68bp1uZa9El6vt/bqAPwurXEkiWjXXUfZOrV6rWwAAAAB8gU8k6FkZmZK2abOOslfssk66Bfi3Ym3b6Fb2Uvfv1y0AAAAAvsBHEvR0O0k/m7AKFXQL8G+h1avpVvYyT3DUGgAAAOBLfGaJe64E6D8Bf8drHQAAAPA7/pWgAwAAAADgo0jQAQAAAADwABJ0AAAAAAA8gAQdAAAAAAAPIEEHAAAAAMADSNABAAAAAPAAEnQAAAAAADyABB0AAAAAAA8gQQcAAAAAwANI0AEAAAAA8AASdAAAAAAAPIAEHQAAAAAADyBBBwAAAADAA0jQAQAAAADwABJ0AAAAAAA8gAQdAAAAAAAPIEEHAAAAAMADSNABAAAAAPAAEnQAAAAAADyABB0AAAAAAA8gQQcAAAAAwANI0AEAAAAA8AASdAAAAAAAPIAEHQAAAAAADyBBBwAAAADAA0jQAQAAAADwABJ0AAAAAAA8gAQdAAAAAAAPIEEHAAAAAMADSNABAAAAAPAAEnQAAAAAADyABB0AAAAAAA8gQQcAAAAAwANI0AEAAAAA8AASdAAAAAAAPIAEHQAAAAAADyBBBwAAAADAA0jQAQAAAADwABJ0AAAAAAA8gAQdAAAAAAAPCMiy6LbjstLTZVOXWEndGKd7shc1aKBEDxuqo3/KTE2VDa3aS8ahQ7rnzBqsXS6BkZE6Mic1PkFOrd+gI/iz0JgYCa9XR0fecWT+AkkYMEhHZ1aiR6zETJ6go3/KysiQuM6xkrJxk+7JXsluXaXKtMk6AgAAAGAKCXoeHJz5vux+bKyO4M+i+veT6Mcf1ZF3kKADAAAA/ocl7gAAAAAAeAAJOgAAAAAAHkCCDgAAAACAB5CgAwAAAADgASToAAAAAAB4AAk6AAAAAAAeQIIOAAAAAIAHkKADAAAAAOABJOgAAAAAAHgACToAAAAAAB5Agg4AAAAAgAeQoAMAAAAA4AEk6AAAAAAAeAAJOgAAAAAAHkCCDgAAAACABwRkWXTbcVnp6bKpS6ykbozTPdmLGjRQoocN1dE/ZaamyoZW7SXj0CHdc2YN1i6XwMhIHZlzcvUaOf7jzzryvuQ/V8jxRT/pyFnlB98rEuS/93kiGp0vxdq10ZF3HJm/QBIGDNLRmZXoESsxkyfo6J+yMjIkrnOspGzcpHuyV7JbV6kybbKOAAAAAJhCgl4IJL45Q/Y8ceZELb8axq2RgOBgHcEt/pygZ6WlSVamsctS4RMgEhgSYv1pNQAA/0ONM8WFj52A4CAJCArSUcFz9fOWzyIg10jQCwESdP/jzwn6tqHD5NSKlTpCfgWVKC61Pn5fAkNDdQ8A4HRx3ftI+lnGmE4oP/wBKX3VFToqWBlJSbLlxlsk4/AR3WNWZItmEjP+CQkIZHctcDYk6IUACbr/8ecEPa7fzXJy8RIdIb9Ca1SXuvO+0REA4N/WtWwn6QcO6Mic6EnjpUz3WB0VoMxM2T50mBz7yp3PhuAK5aX27I8lpFw53QMgJ9zGAgA/Flarpm4BACCS+NEs15LzgLAwqTJ1Esk5cA5I0AHAj4WWL69bAIDCLnndetkz7ikdmVduyL1SrEVzHQHIDRJ0APBjYY0a6hYAoDDLOH5c4gc/KFknT+oes4pdcZmUu+0WHQHILRJ0APBj4TVr6BYAoNDKypLdk56W1C1bdYdZIZWipcr4sRSFA/KAdw0A+KugIAmrUEEHAIDC6vA338rhDz7WkVmBkRES88IzElyypO4BcC5I0AHATwWVKiWBxYrqCABQGKXEx8uukaPtWXTjgoKkwqhHpMj5bK8C8ooEHQD8VFDRIhIYFqYjADArMzNTTp48KYcOHZKt27bJ0qVLJTU1VT+KgpB5KkV2DH5QMo8f1z1mqaNZy/TsriMAeUGCDgB+KqRyJQkICtIRAOSNSrzT0tIkOTlZEhMTZfPmzbJ48WJ57/33Zdz48TJ4yBC5NjZWatetK+fVqyd1rK96DRpI67Zt5bhLiSHOQO07f2qynFq5WneYFd6ooVQeO1okIED3AMgLEnQA8FOhVWN0CwByphLwAwcOyNq1a2X27Nny4ksvyWOjR0u/m26Si9u1k6bNmtlJd6WYGKnXsKG069BBbr71Vnl87Fh5wfpvv5k7V+Lj42Xv3r1y5OhRO6lHwTq66Cc5/P5HOjIrsGhRiZk+RQLDw3UPgLwiQQcAPxVKgTgAOTh27JhcevnlUrd+fYksVkyiq1SRJk2bSq++feX+IUNkwlNPyUcffyzLli2T9Rs2yO49e0i8fUTq7j2yc/gIyUpP1z0GBQRI9JOPS3jVqroDQH6QoAOAnwq/oJFuAcD/UvvDF//+u2zZ6s7RW3BHpvV7jX/oEck4dFj3GGQl51EDbpbSXTvrDgD5RYIOAH4qvEoV3QIAFBb7X31dkn/7XUdmRV7UVCoOHawjAE4gQQcAPxQQESEhZcrqCABQGBxfslT2P/eSjswKrlhBqj43VQJDQ3UPACeQoAOAHwouX04CQoJ1BADwd+lHjkjCsIeshvl95wGhIVJ58gQJKcuNYMBpJOgA4IdCSpaUgEAu8QBQGKhicPHDH5H0XXt0j1ll7rpDirdqqSMATmL0BgB+KKRGNc6iBYBC4sDb78iJ+Qt1ZFbR9u2k4r2DdATAaSToADwlpFxZCa1S2bWvkIoV3Ulkre8RUin6jP8GE18R9evrbwwA8GdJK1fJvqnP6MisEOvzJWbKRG4AAwYFZFl023Fquc2mLrGSujFO92QvatBAiR42VEf/pI6L2NCqvWQcOqR7zqzB2uUSGBmpI/wt8c0ZsueJCTpyVsO4NRIQzD5Xtx2Zv0ASBuR897pEj1iJmXzm33tWRobEdY6VlI2bdE/2SnbrKlWmTdaR/zkVHy9xl3U2flZsYJEiUmfBdxJSJkr3AEDBSkxMlKo1atjHrZmyd9cuiYry9nVvXct2kn7ggI7MiZ40Xsp0j9WRM9KPHZO4bj0lbUe87jEnMCJCqn/wjhQ5v6HuAWACM+gAAACAD9r1xHhXknMJDJTyjz5Ecg64gAQdAAAA8DEHP50tRz/7QkdmlejaWcr06qkjACaRoAMAAAA+5GTcZtkzZpyOzAqrc55UGTeGk0EAl/BOg09R9Qi29rtZ1l3QwvhX3LU9JONEkv7OAAAABS/jxAmJv/8ByUwyP0YJLFZMYp6fZu8/B+AOEnT4jqws2Tf9eUn69XfJOHLU6Fdm8kmpOHqkBBUtor85AKCwyMzMlPT09DN+ZWRkWB9HxurrAjnKsl6buydMylWR13wLCpToMaMkokYN3VF4qfd8TtcF9RjgFKq4FwL+UsX92M+/yI5b7xTrSqh7zCl7/91SYfC9OvIeqrg7hyruvi8lJUXWrl0rf61cKfv375cDiYn6EZGw0FApWbKklC1bVmrXri316tb1fEVpuCcpKUm2bt0qq1avlj179si27dtl48aNcurUKTl58uQZB90RERESEhIipUqVkgb160ulSpWkatWqdrty5coS7EMnm1DF/T98qYr7ke++l/h7hqi7SLrHnNL9+0nlUY8UuiPV0tLSJN4aGyxfsUISEhIkLi5ONllf6rqQnJys/6v/p97zYWFhUrx4cWnYoIF9HahWrZo0btTIvj740jUB3kCCXgj4Q4KeunuPbLZeSxmHj+gecyJbt5QaM9/09F4rEnTnkKD7pqNHj8rcb7+VDz78UH786Sc70cqtOuedJ48/9pj06NFD96AwUMn2tm3bZPHvv8uiH3+Uv/76S1auWqUfdUZ4eLg0b9ZMLrjgAmnVsqW0bNHCHqB7FQn6f/hKgp6SsFPirukumceO6R5zIi5sIjVnvi2B4WG6x3+p17+6wfvLL7/I/AUL5PclS+SYQ89xpJWXtG3TRtpfcom0sK4HzS66yL5OADkhQS8EfD1Bz0xJka3X95eTy//SPeYElS0jtb+eLSFly+oebyJBdw4Jeu78biU169av15EzLrIGKo3OP19HuXPAGkS//OqrMuXpp884k5Fbsz76SLpde62Ozt2sTz6R48eP68h5V15xhURHR+vIGZ999pkcOXpUR867xBqA1vTYUli1HH3Tpk3y2ezZ8smnn8r6DRvsPrcEBQXZN4R69ewpV115pTRo0MCeaTNp1apVsuzPP3WUsxMnTshDI0bYS3RNeXryZClatKiO8q58+fLS+eqrdeQsX0jQs9LSZHO/m+XksuW6x5zgcmWl1uxZElqhvO7xP+o1r27QfWh9Frz73nty8OBB41tXAgICpFixYtKje3fpd/310rx5c+PXgzNRNys//+ILOXLE7KRX6dKl8/U5m1fq53tn5swzroByirrJ0rtXL/sabwIJeiHg0wm69fLc88zzkvjsC1Zb9xkSYF0kq779mhRr2Vz3eBcJunNI0HPn/iFD5MWXXtKRM+675x55esoUHeVMDaZmzZolQx98UBKtgVR+qOWGf/7xh9SvX1/3nBv1sdmoSRPZsHGj7nHet998I506dtSRM5o2a2Yv5Tblnbfflr59+uioYKkVFd9//71Msl5fahCulqwWNDWQU0tfB9x6q/08xcTE2AN2p02dNs1Ouv2NSmo+sBIpEzyfoFvXnF0TJsvBN97SHeaoMV3V11+S4m3b6B7/om7sfj9vnjw1aZKs+OsvV2/YnU6996tXqyaD77/fTvRUMuum2wcOlLffeUdHZqibEbsTElxfMaC2LdU//3yjv9sO7dvLd3PnGrmGKxSJg6cd+22xHHzhFePJufUOkzKDBvpEcg74i4SdO3UrZ4cPH5brb7hBbr7ttnwn54qazVOJEvyPWqr63vvv2zcjevXta88keyE5V9RgcceOHTJq9Gj7Bs+1sbGydNmyAksQfE3rVq10q/BRNXgOzjCbTP2tzF23+2Vyrm7yfvnll9KkaVPp2bu3fW0oyPeeutG7dds2uW/wYKnXsKE8PXVqvlaFnatevXrpljlqlZmqD+O2JUuWGP/d9rFeQ6aSc4UEHZ6VdiBRdj3wsPGZTSWyWVMpf9dAHQFwwxrrg/tsi7jUnuG27dvL7C++cGy5WpkyZew7+/Af6nX0888/S5t27eTmW2+VLVu36ke8KfnkSbuGgvr3XnHVVfYWEuSsRiGtJJ66b78kDH/EyjDNJ5NF2l4sFe69W0f+Q23PurpLF+luJaXqM8VrDh06JA8/8oh9Y/GLL7886+eiEy6xrj2q0KVpc7/7TrfcM3/hQt0yQ60IuC42f8Uez4YEHZ6k9lolPPiQpFsfTKapvVYxz0+XgJAQ3QPADceOHrUrZWdHVc7teOmldlVtJ114wQVG73zDXWof9dAHHpDLrrzSXrLqS9RNJ1XkUN2E6t6zp2zc5MLRWT5I7dNVVfILG7XFM+HhRyTjwP+fTGFKcMUKEjN5ogQY2lNbENSKmslPPy0tWrWShYsW6V7v2rxliz273++mm+x6KyaFhoZKd8NJprJ48WLdcoe6pqoioCapmxvqdBiTSNDhPVlZsu+lVyXpp191hzkqKa80aZyElC2jewC45djx4/by9TPZvXu3XG4lXDt37dI9zimMA31/pWbD2nfsKM+/+KLPLxX/8quv5KLmzWXsE0/YNx3w/0KCg6VChQo6Kjz2v/G2O2OhiAip+vx0vxoLqWMTu15zjTwycqR9PJqvULPnH8+aZc+m/7F0qe4147rrrjN+s1otcTd5SsS/qaMy1VYik27s10+3zCFBh+cc/32JJD7/so7MihpwixS/pJ2OALhJzZ6rs2b/TRX46tWnj5HkXFHVxuH7llqDV7VE3Omj0gqSSiSeGDdOWl58saxYsUL3om7duoXuaKrjfyyVA9Of05FBgYFSYfhQKdKkse7wfStXrrSvDQt8YNY8O3v27rVvUs98911jS95VXQd1DJxJu3bvNp4wn27evHm6ZUZERIR9yoppJOjwlLT9+yXh/gftJe6mRTS/SCoMvU9HAAqCutt9OrU8bcgDD8iSP/7QPc4KCw0ttHtZ/Yk6r/iKq6+W/S5U3i4IaltH3379XJ158rKaNWvqVuGQfuy47Bz+iCs1eIpfeZmU6Xe9jnyfWlLdvlMniU9I0D2+S92sHnjnnTJt+nQjSXqRIkWk2zXX6Micb13ah66eo3k//KAjM9TpKiVKlNCROSTo8Az1QaQKobix1yooqrTETJ1k/Ax3ADn77V/709TxN2rGwJSyZctKKcN7x2DW+vXr7RUWJs+h94LJTz1l7xOFSLOLLtIt/6eOQU0YOUrSEnJ3ykV+hNauKVUmjpOAQP9IB3788Ue5qksXv9oioqrPjxg5Up559lnd4yxVjdy0xS4VwTyVkiJ/GLq5/7cBt92mW2aRoMMz9r/+piT9+IuOzLH3nU8eL6GVonUPgIJy+hL3o0eP2kfOqAGJKeXLl7cLTsE3qWrHsT16yIFE8zdyC9KdAwdKVyvRwH80bdpUt/xf4rvvy/FvzM84BhaJlJjpUySoSBHd49vUqqtu3bvbs87+Rq0sG/bQQ/Lee+/pHue0bNlSihcvriMz1LFnKVbybNrmuDjZu2+fjpynzqpv17atjswiQYcnnPhjqeyfPF1HZpUecLOU6NBeRwAKkpoN/dsbb75p/AicphdeSAV3H6WWL6qCT1u2bNE9/kkVMZwwfryOoPaeV6taVUf+LXnNWtk7eaqODAoMkIpjRklk3bq6w7dt375duvfo4ffFFe+8+27Ht3+pauRXX3WVjszYt3+/7Le+TPtm7lzdMkMtb3friFYSdBS49EOHJGHIcHWLUPeYE9mimVQcwr5zwCvUHmK1VDkxMVHGjB2re81RxabgmxYtWiRvzZihI/+k9oS+/eabUrRoUd2DkiVKSFRUlI78V/qxY7Jj8IOSddJwxfGAACnd73qJ6nat7vBtasa8d9++dhLo71QRyRv69bNXEjnJdFVyNXv+7+1sTlOrDEzudVc39u+4/XYdmUeCjgJln3c+bISk796je8wJKlVKqkyfzHnngIeo5ez79u2T995/X5JzOBPdKer8Uvge9ToZNXq0PQjzV2oAOOKhh6RJkya6B0r5ChXsysl+LStLdj05QdK2/bNopgnh9etJ9EMP2om6Pxj75JOy3MUTD4KCguxio2plh/pSW6ZCrHGlWyuzdsTHy52DBjl6LWzerJmUKWP2iL1vv/1Wt8w4euyYrDttRZ7ToitWlGbW8+QWEnQUqP1vzZATC37UkUHWhTN6/BgJLYTnqAJepqpUr9+wQV562fzRimogVa1aNR3Bl6iq7aYq+3uFOvJo6JAhOsLfGjdqpFv+69CXc+ToZ1/oyJygMlFS9aXnJNBPjqz7+eefZeq0aToyRxVrvOLyy+WF556TX3/6SbZt2SJ7d+2yv/bs3Ckb1q6Vb+bMsW+w1a9XT/+/zPnK+l5OzharquQdDB8/+rN1Dc/IyNCR89TJF06vLDhd+/btjR9JdzoSdBSYE38skwNTntGRQVZyXvq2/lLyyst1BwAvefOtt2TL1q06Mqdq1aqufsDCGWrv+bPPP68j86pUqSI333STPD15ssyzBsEb162TrXFxsm/3btm0fr2sW71a5n//vTz3zDMycsQIuaZrV6lXt64E5+NUkKjSpeWdGTPsmTj8U+3atXXLP53avkN2jx5rz6KbFBASLJUnPCFhflIgV+03HzBwoI7MULPivXr2tN/zc778UgbefrtdsFCdBqK2o6gvtSc5JiZGLu3UScaOGSPLly2T2Z9+Kuc3bKj/FuepFUX3Dx7s2J579XPecL3Zo/ZUYc+9e/fqyHnfWddkk9xc3q6QoKNApCUelIT7H3DnvPMLm0jFB5mVALxqztdf65ZZlaKj85VEoWAcPHhQfvr5Zx2ZU716dXlv5kw7IX/t1VflvnvvlfaXXGKfm6+SdlXBV/03KmFs166d3HnHHfL46NHy6axZsuLPP2VXfLx8/OGH9kC3SuXK+m/NnSmTJkmM9T3wv/x5W0pGcrLEW2OhzOPmi5tF3XaLlOjYQUe+b9KUKbLVYFHR4lbiPXPGDHn3nXfsm7u5pZbAd+ncWRb/+qud0JuyfccOef6FF3SUf5dY1zpVMM6UZOu1/qd1nTRB3cT94gtzK1DU7/8il496JEGH67IyM2XX6LGSvtfcUQh/U8u5Yp6dKoEcqwQUem5/wMIZq1evto/gM+ni1q3lj8WL7dmyvMxiq0G5SuBju3Wzi7ytW7NGflq4UHr26HHWI4xu6NtXbrjhBh3ln7pxsNMavOfma9WKFcbPWl/1119n/N65/VL7Y/2RGgvtfmqKnFqzVveYU6RNa6k49H4d+T61D9vJ5PTf1EqrD99/X3r36mXPLueF2lL17PTpcv+99+b57zibqdbf79S1Ua0GuLRjRx2ZMX/BAt1ylqoQb/J0j8svvdT11U0k6HBd4tszXTnjU4KDJHrCExIaXVF3ACjMateqpVvwJaZnz1Vi/cF77zk6e6SKR7Vq1Uref/dde3nsxPHj7RUc/x6oq5n2p59+2tEBvEou1Hn/uflSS3VNK2d9jzN979x+qZsf/ujoDwvk8Acf68ic4HLlJGbyRAnwo+dxivWeUad/mDJ61Ci57LLLdJR36rU7ftw4Y6tADh8+LK++9pqO8kddg9QNSpN++fVX3XLWXytXGi0ya7rK/ZmQoMNVSStXyb4p5gt6KKX69paSnfxnOReA/FGzpPA9JivzKpdbA/GKFc3dyFVJ5gNDh9qz6mrfutqvqqhK0G++/rq9/xyFS8quXbJzxCgRg0WzlICwMIl5fqqElDN/I8YtO3fulLcNHrfYonlze3uLU9QKlReff14iDZ1E8Jp1DVF70p2gbkoUMVinZc3atfZNBactMDQzr6itR82t14TbSNDhmvRDhyXh3qHmz/i0hDeoJ9GPPqxuCeoeAIWZWm4Ycw77COEda9et0y0zqrtU2V/NbN8xcKC9rHzUyJEyePBge98nCpdMfbxs5pEjusecwKJFJMzPTq6Y+e679nngpowZPdrxWiWqbsWtt9yiI2dt275dFi5cqKP8KVq0qHTp0kVHzlNHw6lq7k5S+89NFojr2rVrgaziIUGHK7LS0yXhoUckLWGn7jFHfSBVeX6aBBreVwegYKgjYa6+8koZ/+ST9pE3CdYA5ejhw3LM+jp66JBs37JFfrMGAWr/31133GGfK93m4ovtGUv4nsTERN0yI82FYqWnU3s9Hxs1Sp4YM8bY3lR41/6XX5PkJUt1ZFbGwUOy8/En7f3u/uDkyZPynMG9561atpSOhvZh33P33cb2Mb/l4IoCVTfDpCVLluiWM/bs2SMbN23SkbOCAgPl+r59deQuEnS4IvHDj+XE/EU6MicgOEgqTZ4g4Zx1DPidqKgoefyxx+wq2198/rkMe/BBe+lZhQoV7OWDEdaXmqWsVKmSNLvoIrnrzjvl2WeesYt/fTF7NskQzihu82Z7FsZtvB4Lp+AyUbrljuNzv5NDn3+pI9/2w/z5cuDAAR0575abbzb2vqxmjUsbnX++jpyl6nQkJSXpKH/atW1rf5aaov6tTl5vf7cSfqeW+P9b5cqVpemFF+rIXSToMC55zVrZN26SWoeie8wpqfadX5H/wh4AvEMNl9SxNSuWLZORjzxiJ+rnQg241BJ3+CbTieyChQtlm8HjmoDTRfXsLpHNmurIBdbYa8+TEyTNYGLrllmzZumW89QKq64Gl3erZdLXxcbqyFn79u2TpUudWZVRqlQpu2q5KWofempqqo7yb9Eic5N/3bt3L7AilSToMCrjxAlJGDpcsgzuF/pbWP26Ev3IcDWa0z0AfJ36cLz//vtl1kcfGS3kBe8yfcyWqgZ9ddeukpCQoHsAcwKCg6XSE49LgIvHNmUePSY7R41xZaLElJSUFJnzzTc6cl4z6zpTpkwZHZlxmcHE9+NPPtGt/OvVq5duOe+ElRcsX75cR/mnbrCaoG7Y9L/pJh25jwQdxmRlZMjOkaMlNc7c2YR/CyxWVGJemC6B4eG6B4A/GHTnnTJp4kTHi/bAd1RzobifOkO3WcuW8sGHHzo6uwOcSUTtWlL2vrt15I7j8+bLQR9e6v7rb78ZPVqtk+EzwJW6devqlvNU8TVVhM0J6lg4VSvDFLVVwQm7du82tv+8Zq1acl7t2jpyHwk6zMjKksT3P5RjX5m72/lfgQFScexj7DsH/Eznq66SyZMmGV/iDG9r1KiRbpl18OBB6X/LLdK0eXOZ9ckncvToUf0I4Lxyt/aXsLp1dOSOveMmSuqevTryLV9//bVuOU99xnRo315H5oSHh8v5DRvqyFm7rWT10KFDOsofdTRkG4PHkqrz0J3Yhz537lzdcl732NgCnRggQYcRyes3yL6JU9zZd96zu5S+tquOAPiD0qVLyysvv1xg+7/gHepcYrdu0qhB44YNG+T6fv2kboMGcu/998uyZcvs5bWAk9SKv8qTxkuAi6dLZBw+IgmPjLJXOPoSVQTsx59+0pHz1PFi6ig009R1rGKFCjpy1rFjx+yCl07p37+/bjlv06ZNjqxUMra8PSxMbr75Zh0VDBJ0OC7j+HFJuGewZCWf1D3mhNaqIZVGj2TfOeBH1CDmybFj7bv4QI0a1nU+OlpH7lHHu738yivSqk0badSkiTwwbJhdiMntY9ngv4rUryel+/fTkTuSfvlNDs3+Qke+QS1tX79+vY6cp07/KFmypI7MKmHw+6xZs0a38q/9JZdI8eLFdeSsnbt2yfbt23WUN+qm6dJly3TkrAb16xfIZ87pSNDhrKws2fX4k5K6bYfuMEftO6/68vMSaPA4CADuU/v0buzn7qAV3qUGz7HduumoYGzdtk2efe45ad22rdSoVUv63XSTfDxrll09GcgzNaM6+F4JqRqjO1yQmWlXdU/Zbn6c5hSVzKUavDGm9hqHurSSoVzZsrrlvN8WL9at/FOnpbRs0UJHzpv77be6lTfxCQn5TvKz0+3aawt89R4JOhx1cNancnS2C0VIrDdOxdEjJbxmDd0BwB+o2fOHhw+39+oBf7vn7rs9c1TeXisp/+jjj+WGG2+UKtWqSYtWrWTM2LGy6McfjRaxgn+yl7pPeMLVlYCZx09IwqOjfWapuyqAZlLVGPdukJQrV063nLdnzx7dyr/AwEC5yeCN8vxuWfjhhx90y1mhISFGl/fnFgk6HHNy4ybZM/pJV/adl4i9RkpfV7AzKgCcV7lSJfvuNXC66tWrS4/u3XXkHWrP+vIVK+TJ8ePl8iuvlErWQL9Xnz7ysZXAq8GyE4WQ4P+KtWgupa7vrSN3JC9eIgdmvqcjb1OnLJhUqnRp3fJtcXFxuuWMK664wtjKgpUrV+a5toe6rn5j6Mi9pk2bGqsTcC5I0OGIjKQkib/7fnfOO697nlR+YjT7zgE/1Kd3b3tJM3A6tbJizOjRUqxYMd3jPWrQePLkSZn9+edyw003Sf2GDeWKq66Szz77jJl1nFWFwfdKkOFzuP9t/9Rn5JQPLHVXN8FMenvGDKleq5YrX09Pm6a/q/MOHjokpxwch5coUULatmmjI2eplUjqmLS8UNfTPw29Jq695hr786agkaAj37IyM/+z73zLNt1jTkBEhFR55mnOOwf8kFpaNuC223QE/FPVqlXliTFjPDF4yo0TSUmycNEi6X399VKvQQO7yNyOHTuYVccZhZQuLZWefFytLdY95mUmJcvOh0dKVnq67vEeVZTRyaXbZ6ISvp07d7rypaqtm6LOQVc3CZ2irrU9e/TQkbPU71UV3cyLzZs3y4EDB3TkHPXz3mBdr72ABB35dviLr+Top5/ryCDrjVPxsREScZ75ozAAuK9+/fp2EgZk546BA+Xqq67Ske/Yt3+/XWSuVp060veGG2TFX3/pR4D/V6JjeynWqYOO3JG89E858P6HOvIetQz6FMcc5k5WluOnTJicUf5qzhzdOjfzDO0/v7h1a6nggeXtCgk68kXtO989YpR9UTCteLeuEtW7p44A+JtOnTpx7jlyFBwcLDPeeksuaNJE9/ieTz/7zC4spxJ1dR4w8LcA6/pXeexoCSrlzpFff9s/aaqc3OTs/mWnqPOy87pXubDJyMx0fIa+TJkycvlll+nIWUv++EMy8lCo8Lvvv9ctZ/Xt00e3Ch4JOvIl/t4hkpWSqiNzQuvUtj60HrNn0QH4p+s99OEI71L7Ir/+6itp0rix7vE9apn7J59+Kk2bN7crwCcnJ+tHUNiFlCsrFR59WEfuyDx5UhIefFgyrWTYa1TCefToUR2hIPTp1Uu3nLVv71572f+5OHjwoPy5fLmOnBMREeGpArUk6MiXNJfOO495dqoEFS2qewD4m0rR0VKvXj0dATkrW7aszPvuO7n80kt1j29SBZ1UBfiL27a191UCStQ110jRDu105I5Ta9fJ/ldf15F3qJtZ1G0oWB07dpSQkBAdOeekdf071+0+a9audXSf/d/aXHyx0SPwzhUJOjyv/MMPsu8c8HNNmjQxMgCA/ypZsqR89umnMuT+++0ze32ZGnS2veQSmfvtt7oHhVpggESPGikBLp/9f+DFVyXZStS9JN1Hzmr3Z9HR0XJxq1Y6ctbXX3+tW7mzaNEiIzdsbjR45ntekKDD89JU9U7ungJ+rRrF4ZAHYVYCM+mpp2TWRx9JlcqVda9vSjx4UHr06iXvvf++7kFhFl41RsoPG+Lq1r6slBTZOfIxyXK40Fh+sP3DG/r27atbzjrX5erz58/XLeeobVOm9tnnFQk6PO/gq2/KsZ9/0REAAP90Tdeusuqvv+zZdDXY8lWqINaAgQNl1ief6B4UZmVu6CvhDdzd+nNq9VrZ+8JLOip4xdjemGtqJVFkZKSOnNWhfXv7hqjT1Oqh/fv36yhniYmJsvTPP3XkHHXWe1RUlI68gQQd+RLZoplumZOVmiY7HxwhqbvNnoMJAPBdRa2BvJpN/8sawPXu1cvYQNW09PR0uf2OO2T5ihW6B4VVYGioVJk0QQLC3V3qnvj625K0arWOCpapI778kXqm1EkXJqgjUBs2aKAj56jl6r8tXqyjnC3+/Xf7+ui0/jfdpFveQYKOfKky9SkJKmP+rlPGgUSJH/yAJyuMAgC8o3LlyjJzxgxZuXy53HvPPRJVurR+xHckJSVJ/5tvZnkv7Bo8ZW6/TUfuyFJV3Yc/Yld3L2iqNgn1SXInwOAMupqdv7l/fx0567ffftOtnP3000+65Zzy5crJpZ066cg7SNCRLyHWC7vKM1MkIMTMHbvTnVy6XPY9+4KOAAA4MzXrVq1aNZk6ZYps2rBB3nrjDWnVsqVPzcZt2LhRJkycqCMUWtZrtvydt0torZq6wx2pcZtl74sv66jgqISzSJEiOkJOVBIdGhqqI+d1vvpqCTPw96uZ8dwUfvs1l4n8uVDV29XqK68hQUe+FWvdSqKsDw83JL78uhz71fk3KADAPxUvXlz63XCD/LRokWxct04mjBtnD8pMDDSd9tIrr8i+fft0hMIqMDxcKk94UiQoSPe4Q425Thg4c/pcqH3P4S5Xs/dV5cqWNZqgq2rujRs31pFzVq1aZR85mRN7//myZTpyTu/evXXLWwKyDB4umJWeLpu6xErqxjjdk72oQQMlethQHf2TWta8oVV7yTh0SPecWYO1yyXQR/ecmZT45gzZ88QEHTmrYdwaCQgOtn/X2269Q5J+/lU/Yk5wubJS66vPJMT6s7A6Mn+BJAwYpKMzK9EjVmImn/n3npWRIXGdYyVl4ybdk72S3bpKlWmTdeR/TsXHS9xlne3XsEmBRYpInQXfSYgLW0JMuH/IEHnxJXOFg+6+6y6ZPm2ajrxNfWw2atLEnuE05dtvvpFOHTvqyBlNmzWTVavN7St95+23pW+fPjryNvU7PHbsmHwzd64stBJ3tcRyy9atRvY35tewBx6Q8ePG6chZatBbtUYNuzidKXt37fJcAaZ/W9eynaQfOKAjc6InjZcy3WN1dO52TXhKDr7+to7cEVI1Rs6bM1uCCmh8rd6TdRs0kB07duge56nVNu3attWR7zqvdm15aPhwHZnx0ssvy32DB+vIOQt/+EHatGmjo//1yaefSt8bbtCRM9S551s2bZLw8HDd4x0k6IWAGwm6knbwoMRd3U0y9pv/kIts3VJqvP2aBBTSfUkk6M4hQc8dEvT/R4J+Zr6UoP+bSgLUTLVK2NWXmqlRlYUNDpFyTc1aqZl/E4NIEvT/8JUEPeP4cdl49bWS7nLR3NL9+krlx0fZy+0LwiUdOuS6kFheXHXllfLl55/rCDlR18VqNWtKmsNH8Y146CEZO2aMjv7XgNtvlxkzZ+rIGX1697brlXgRS9zhmBDrA7jylImuLMFKXrxE9r7wshop6x4AAPJGVT6uVKmS3D5ggMz+9FPZsHat/Pzjj3LXHXdI1ZgYY5WRc2Pv3r2yctUqHaEwCypWTCo9aSUxge4O3w9/OEuO/1lwS92bXXSRbpmxes0aycjI0BFyUrZsWWnZooWOnJPTPnR1A3HxkiU6ck6P7t11y3tI0OGo4m0vljJ3ubAf3XoTH3zxVTn+x1LdAQCAM1TRoBbNm8uzzzwj661k/aeFC2XQXXcVSEX4zMxM+frrr3WEwk6Ns0pc01lH7lArzHYOf0QykgrmVIHatWvrlhknTpywt7zg7FShzWu6dtWRc9SKtJSUFB390549e2TLli06ckb58uXtlRNeRYIOx5W/Z5BENDd7t1PJSkuTnfc9IGkuLKkHABRO6oinZs2ayTPTpsnWzZvlpRdekNq1aulH3bHkjz90C4WdOkor+uFhEhTl7s2itB3xsnvi5AJZudjCwIzt6VRyvinu7Ntx8R/XXnut4ydiqJVC27dv19E/qTohTq9w6NK5s9GCevlFgg7HBYaFSswzUySobBndY066lZwnDHtYstJZmgQAMEsd+TTgtttk5YoV8uTYsRIREaEfMUvtiWcJLv4WUrasRI8e6fpS9yMffyLHfjO3Fzw71atVM3rUmlql8sMPP+gIZ6N+H82bNdORc+Zks1LI6RVE6ji63j176sibSNBhRGiFClL56Yn/LSBnUtJPv8q+51/UEQAAZqlZdVUt+fNPP5UiLhSnVUXsfHUJrhcr4/uDkldeIcUudbaQ5NnYS92HjZD0w4d1jzvUlpP69erpyAyVHHqhKKSvMFEQ9EyFAJOSkhzff17RylFat26tI28iQYcxxdtcLKXvuE1HZiWq/ei/swQQAOCeDh06yPBhw3RkjprhU/tknaaK3zm9VPXf2NtrRkBQkFR6/FEJLFFc97gjfd9+2TV+kqtL3YOsn/XSTp10ZMZfK1fKtm3bdISzueLyyx0vnrl8xYr/OQ9dHX+pKsc7qVu3bvb5+l5Ggg5zrA/9ivffIxEtnV8G82/2fvQHHnL9ri4AmKASMiepmSGnj8XBfwom3XbbbcaXuqvf378Hrk5Qg1TTCbrJI9wKu9Dy5aX8g0N05J6jn38pRxf9qCN3XHHFFbplhlrp8ZZHj9zyoho1akjDBg105Ax11OWu3bt19B+LFy92dGWDutnTz+Hz1E0gQYdR6pzyKpMnSlCpkrrHHHUuaMJDI42fZw0Aph0/fly3nPHJp5/K+g0bdAQnlS5Vyt6T6YtMJ+fKZoerL+OfyvTqKRFNL9CRSzIzZddjYyX96FHdYZ46VUEd8WXSjHfesZdU4+zUPu4b+/XTkTPUTZLffvtNR/8xZ84c3XJG1apVpdH55+vIu0jQYVxY5UpSaepT9nIs007MWyD733hbRwDgm5xc0hcfHy/3DR6sI/9y8OBBOXCgYE/yUANVtSfdNDXz47Tw8HAJNJykq2WrMCcgOEgqj39CAiLCdY871KTIzkdH28m6G9Ry6l6GC3up47zGPvGEjgqe0yupnKaOKXO6Evrcb7/Vrf/cqP7lXwl7fl17zTWert7+NxJ0uKLEJe2k1K036cisA1Omy/HFzhaUAAA3rXAoqVH7f/vdeKMkJibqHv+hkvOu114rzVq0sCswF+Rg1vRuXJWcFy9uZq9x48aNdcsMNSNG8S2zImrVlHL3DrK3Frrp2Lffy5H5C3Rknqq8bXrVx6uvvSZr163TUcFQ25Heffdduenmmz1dZLFatWpSvXp1HTlDFYr7+2detWqVoysa1M3UW/r315G3kaDDHdYFteKQ+yS8SSPdYc5/qow+LOmHj+geAHCOGiCWKlVKR2aoY7Xym9SkpKTILbfe6ngFXC9QMyvXxsbaz5Pas9jFStT733KL7P7X/kU3qL3hpm+AhAQHS4kSJXTkrMqVKumWGcv+/NM+4xhmle1/o4TVq6Mjl2Rmya6RoyXNpVUszZs3N17N/YSVEKqbmkddXL7/N3XNX7lypVx6+eVyy4AB8vGsWfLsc8959gaXWjnU7/rrdeQMdeN1165ddlsl607+7OfVri21rS9fQIIO1wRGREjMc1Ml0NAswOnSd+35z/noHl8eBMA3mS4KtnrNGlm7dq2Ozt3JkyflZis5/9Lh/XteoKqZ9+7bV5b88f8nd6gzwj/86CNpfOGF8tSkSUYqnmfnp59/Nn5joEmTJsaW0VcynKCr38XjY8bkeaDNMW25ExgeLlUmPGnX/nFTxsFDsvOxsSq71D3mqJUkQ1zYrrPGuvb26tPH8VogOVFJ6W233y4tL774v8eNqffMqNGjZf78+XbsRdfFxjq6/Ubd8FQ39RSnz6bv0qWL45XnTSFBh6vCKleWSlMm6MisE/MXyYF33tMRADjH1Gzm6cZPnJinpEYN9GK7d7cLw/kbNWDue8MNMi+bgduRI0fk0ccekzr168u06dONz2yr/e+Dh5ivon3hhRfqlvNat2qlW+bMfO89eeONN87p9bxp0yYZPHSotGvfnkrwuRTZoL5E3eLOdsLTHZ83Xw598ZWOzFIJYaXoaB2Zs2DhQmnTrp2sW79e95ixdds2GTZ8uNRr0EBmvvvu/9yQUq99tTpo+/btusdb1BL3enXr6sgZ38+bZ2/P+vHnn3WPM26znkdfQYIO15W8rJNE3TlAR2btnzRVklat1hEAOMPp42XORCXYL738cq6TGjXz8N7778tFzZvL/AXu7Qt1i1qyP2DgQPn2u+90T/ZUkb3hDz8sNWvXtgvkLV261PFj5rZZA+bOXbvaA2yT1L5Jk8WxatWqZX8Pk9Rzf89999mJxurVq8+YcKvX70YrKX/nnXfkyquvlkYXXCAvvPiivY1BJS7IhYAAqXD/PRJavarucIl1jdoz7ilJdWErQ7FixeTRkSN1ZJZKzlu2bm2vyjns4DG+aoWTmhVXK4HqN2wo0599Vk7mcIzi/gMH5NrrrnN1ZVBuqZU9vXv10pEzVN0Kdc12sq7IBdb1RB0N5ytI0FEgKgy+T8IamN1HpGRZF8GE+x7gfHQAjlLFcUxTifnQBx6QIUOH2rMnahn3v6k+dXasKijUtFkzueW22yTx4EH9qP9QyZv62T6bPVv35E6y9RmgbnK0bd9e6jZoII89/rgssxI+NTuTl9UJasCoKj1PfOopaXLhhbLir7/0I+ZUrlxZzrcG8aaoGbCSJc0fhZphPXcffPihNG/VSirFxNhJuNqG0cdKUlpdfLFUrlpVLrCe09sGDrRvMJ3+ele/N7U3FWenlrpXGjdWrQfXPe7IOHRIdo4cLVkZ5rcWXn/99UbfE6dTybRalVO3fn15cPhwWb58+Tknyuq1rFbzqO0walVInXr15KouXezr2Zmu62eybt06ueOuuzxZ2V0l6E7e5NuwcaN89vnnebpGZ0dVbzd9I9JJAdYP79xP/y+qWNemLrGSujFO92QvatBAiR42VEf/lJmaKhtatbff/DlpsHa5BEZG6gh/S3xzhux5wsyy8oZxayQgj/s5Tm3bLlu6XieZScm6x5yiV14m1Z6f7spRb25QVVMTBgzS0ZmV6BErMZPP/HvPsj4Q4jrHSsrGTboneyW7dZUq0ybryP+cio+XuMs6Gz8/P7BIEamz4DsJKROle3zL/UOGyIsvvaQj591tDTymT5umI+/7a+VKad6ypaMDiJyo47BqWImUOgv472RKLef+fckS2blrl6t7JbPzzttvS98+fXTkHDVzrhI5p5bsqyJ/ZaKi7JssHTt0kPPPP98uPFW6dGm7UrraT6m+1MBZfamZM1WI7pdffpHvvv9e/szDAD0/Hn3kERltJQgmXdutm3xz2vFGXjT4vvtk8qRJOnLWupbtJN2FQmfRk8ZLme6xOjLIui4lPDZGDr//ke5wifXeqvTUOIly4Wf82Xo/qmJqBZGwli9f3i441rJFC6lQsaJ9bVbXjrDQUEm3rhlqmbq6qaqKI8bFxcmvixfLgf375eixY/pvyLunJkyQoS5sqzkX6nfQuk0b+9rolL+vwU5Q1/y1q1b5TIE4hRl0FJjw6tUk2rqQiwt3tE58O08OzJipIwDIHzU4i7CSZreoGWS13PKtGTNk2jPP2F+qvX7DBk8k56aoga7a4+3kfnp1U+VAYqK9dPqpyZOl3003yYXNmkm1mjWldNmyUrVGDXvZaYyVwKu45nnn2fugH3n0Ufnxp59cTc7VTYN777lHR+b0MXBjxWkvvfKKbLKSHeSCWuo++F4JKldWd7jEem/tnThZUvft0x3mXNy6dYEdmaVWLakbBJOffloeePBBu+ZHp8sukzaXXCLtO3a0bxyo7Thq5n3GzJmyefNmR5JzZfTjj9v7471EzUx369ZNR85wKjlXLmjSxKeSc4UEHQWq1FVXSKm+zu5dyc7+ydMleW3Bnm0JwD9ERkZKhw4ddAQTVHJ+/+DB8vqbb+oed6iVCfEJCY4NqPNj0J132km6aZd26iTFixXTkTeplRQPPfywa6tWfF1IVJRUGjPKlUmQ02UcOiwJwx8xvyrN+rmenjLF8QJlXnfKeh/c1L+/7NixQ/d4w5WXX27PVHvRDQ4fBecGEnQULOsCGz1qhISfb77gUtapU5Jw71DJOO69IhsAfE/PHj10C05TSyYfHDZMXn39dd1T+DSoX18efughHZlVpkwZueyyy3TkXV9/840s+vFHHeFsSlzaSYpd3klH7kn6dbEcnGX+FIkiRYrIzBkz7MJxhcm+/fvtWXsvrZ5q1KiRJ4uwhYaGSteuXXXkO0jQUeACw8Ik5oVnJLBYUd1jTuq27ZLw0Eh7DzYA5IeadVQDRDhLLW18ctw4efHll3VP4aMGla9aP3+Y9fnoBjXz9cjDDzt6nrEJavZ8+EMPcexaLgUEBkrlsaMlKMr8Kox/sH5Pe8ZPklM74nWHOY0bN5Y3X3/dfs8UJqvXrJFB99zj6FLw/FArGkzUIMmvphdeKNWqunyqgQNI0OEJYVUqS/STj9sz6qYd/26eHPzwYx0BQN6oQkGxDu+7My0qKkouaddOR96jErBJkyfLuAkTCu1SZpUkPzNtmjRv3lz3uEMVy+vapYuOvEsVaHxnJjVlckstda/w0IP2vnQ3ZSUny87hIyTLhSJuqkL3+Cef9OwSa1M+/OgjmfL00zoqeF07d7YTdS/pf+ONPvm6IEGHZ5Tq2llK9rpORwZZHxZ7x0+S5HXrdQcA5M2wBx7wmZkb9e987eWX7UrwXqUGUl2sQV6tmjV1T+GiBrcPDx8ut916q+5xj3ruxz7+uGuz9vnxxLhxdq0A5E5U7LVSpO3FOnJP8rLlcuCtGToyR712VTHFxw2fduBFU6dP98wRhOomX6XoaB0VvKJFi8pVV12lI99Cgg7vsC6w0SNHSFgd85UWs5JPSvxd90mGH1c/BmBevXr17Dv0vkANXtVevJiYGN3jTWqQ9/vixfbZuoVpRiw4OFhGPPSQPDZqVIH93Or1rI518/rzvnv3bhk/caKOcFaBgVJp9EgJiIjQHe7Z/+yLkhJvfqm7urk14uGH5ZWXXpLIAvg5C4Javr1w/nx7ZZQXhISEyI39+umo4F3UtKlUrFhRR76FBB2eElS0iMS8+KwEFjdf8CMtPkF2jhxt75UCgLxQicyTTzwhVSpX1j3eo/6NI0eMkAcfeMCO65x3nv2nlxUrWtQu/vTBu+9KxQoVdK//UufcPzt9un3eeUEvEX1g6FDp0L69jrzrRSsR27hxo45wNuHVqkn5YUPsyRA3ZZ44IfFDh0umC3UD1LXu1ltukdmffSZly7p8xJyLSpQoIU+OHSs/LVok9evV073ecF1srGdqWVzft6/nbzZmhwQdnhNeo7pUfMJKnF0YpBybM1cSP/hIRwBw7tQxWK+/+qonl7qrWVk1I3r6rGy5cuXsP71O/Xu7d+8ufy5dKjf16+cTS6/zomrVqvLNnDly+4ABnhhMqlmw92bOlEbnn697vMk+dm3EiEJbqyAvyvTpLeEN6+vIPSf/WiUH3npHR+Z17NBBfv/1V/usdF9N0M5EJb6XXXqp/PnHH/LQ8OGe/MxRq3C8MGutKvur2gS+igQdnlSqy9VSsqcL+9GtD/Z9456Sk3GbdQcAnLuOHTvK1ClTCnz283RqmecLzz4rj44c+Y9/V6lSpXxq0Kpmwl5/7TX5+ccfpXWrVp56jvNDJcK39O8vS377Tdq2aaN7vUEdu/bF7NmeT9LVsWvz5s3TEc4mMCxUqjw1XgLcvtlljbUOPP+Sq2MttZXnu7lzZdwTT9h7kX2Zul43aNBAvvjsM5nz5Zf2TT2vUjcNenbvrqOCo66p6rPOV5Ggw5PU0SDqfPSwuuaXYmaq/eiD7peMpGTdAwDnbuDtt9uVhL2QQKol93O++kpuvfXW//n3qJkFVYHel6gB6gVNmsiCH36QL63EUSXqvkztjfzeSh5eefllz+wf/bfK1mtIJThqNtKL1GtCnUhQycPbS7wo4rzaUmag+0UIM5OTJWHYw5KZlqZ7zFOrboY9+KD8sXixdLvmGp+cTa9bp468/cYb9o28K664widuUHrhuDVfr2FCgg7PCipSRKpMmyyBLpwznLp5i+x6bIxd4R0A8kINBtT+3bdef93eQ10Q1L+h3/XX28vCs5uVVTMcBfXvyy+1xFMNUhctWCAL5s2TXj17+sxZ9Op3oxLz9999V379+WdpY/1+vD6AVDPpasZOFa8L99AWA1Uc64P33pPvv/1WGtR3f8m2T7Nec+XvulPCrETdbadWr5V9z72oI/fUrl1bZn38sfy4cKFccfnlnj/vX1HL8z98/31Z8eefcr11TfelLT5qmXv16tV15L7ixYv7xJGROSFBh6dF1K0jFceOsl6p5l+qRz//Sg7O/kJHAJA3ajC1dMkSV88bV3vN27VtKz8vWiRvvvFGjkv71NJqNYvuy1Ri29b6edVe6a1xcfLUxIly4QUX2M+D16iCTj2uu05+XLDATsx79ujhU8v01etl7JgxsmTxYunUsWOBPceqkJ76/p998on9PHa3nlN/2e7gNrXUvdK4MerCoXvck/jqG5K8vmCOuW3VsqV89cUXslzXtSjjsdUrau+2OmLxLyspV6uF1Gvci9e0s1Hv1dhu3XTkPnWd8PnPuCyD1TWy0tNlU5dYSd0Yp3uyFzVooEQPG6qjf1KVHze0ai8Zhw7pnjNrsHa5BEZG6gh/S3xzhux5YoKOnNUwbo0EGL54ZGVmSsKIR+Xox5/pHnMCrIFIza8+kYg6dXSPNx2Zv0ASBgzS0ZmV6BErMZPP/HvPysiQuM6xkrJxk+7JXsluXe2VDP4q7cAB2TVuovWcmF09YQ+IHntUgl04ocCEGe+8I/OsAYMpl3bqJDf3768j/5Bhvc++mjNHxowdK+s3bLBjpxWxPvNUovrIww9L8+bNcz0zpCpg/2YlXE4adOed0rp1ax25Tz2/O+Lj5QtrAP659bV23To5evSoftQ96uZByZIlpdlFF9mJeTdroOrLeyFPl2l9Hq9YscI+4mzBwoVy4sQJ/YgZatawRo0a9p7W/jfdZC+7N5GUJ4wcLenHjunInKjr+0jxVi10VPD2v/m2JK1YqSP3hJ9XWyrec5c9m1+Q1PVh7ty5MmPmTPlz+XI5fPiwfsQd6nqtiox2uOQSOzFv2bKlRPpJHqM+88aNH6+j/3UyOVm+tp57E5+L6satWl3ly0jQCwFfT9CVDOuDc/N1vSV1yzbdY05Y7VpS68tPJDA8XPd4Dwk64DvS0tLkj6VL5ZVXXpG5334rx62kJq+DkkBrQKtmNFUyrgYgna++2k5avL5U2m1qaHPw4EFZvXq1fPvdd/bge8kff0i6NS5RX05SM1xqoK0S8hbW7+Vq63eiiqupJN2fqbPIv7EG2LM++cR+bk+ePGkn8PmhXttqxUFrK1FRS1TbtWtnF8TyhSXJ8G1HjhyxrxOq8ODixYtl9Zo19rXCyQRSXSvUlhx1rbj8ssukffv29h7ziEJybvvpPps9W3r37asj56jnd1d8vM9sfcoOCXoh4A8JupK8br1s7XmDZCWbL+ZWsncPqTLhiQK/u5sdEnTAN506dUrWWAM/lbD//vvvEp+QIIlWIhlvDShUgnM6tf+3fLlydhExVfStWbNm0rBhQ2ncqJHfJ38mpFpjiZ07d8qatWvtPzfFxcnWrVvtgbmaSUtKSrJn4M9E/Q6iSpe2n3e1v7GalTTWq1tXqsTE2L+TypUqFcpB9t/Uc7fWel5Xrlol69evt2fP1Gykel63bNki/x5oVoqOtmcO1Zc65/6CCy6QmjVrSiPrtR1TpQoJOQqcWh2ybds2eyWOul6om1DHjh2zv9Q1Y/eePZJ8hvGoSgyjK1a0bzSp64W6gapu2Kmq8udb14oq1utb3YgqzNRnXYvWre1rhdP63XCDvPXGGzryXSTohYC/JOiKOrN8zyOjdWRWpWcmS+lruurIW0jQAf9xto9hZsfNy+1QiN/FucnpeeW5hK/KzfWC13f2Xnv9dRl0zz06co66sffDd9/ZBTh9HQl6IXBk9hdy4OXXdeSsWl9/biXoLt7ptl6uu6c9Kymbzv6ayq/AYsWk8phREuTB1xQJOgAAAHzJvn37pPEFF8jBs+R0eaG2C/y1fLlfrMAhQQd8EAk6AAAAfIVKOW+59VZ574MPdI9z1IqF1155xS4m6Q84nwIAAAAAYMz7VmJuIjlXypUrJ9fFxurI95GgAwAAAACM+PXXX43sO//bnQMH+vzZ56cjQQcAAAAAOG7V6tXSq0+fM1a9d0LFihXlvnvv1ZF/IEEHAAAAADhq/oIFcvkVV8j+Awd0j/OGP/igffylPyFBBwAAAAA4Ii0tTZ5/4QW5NjbWSMX2v9WtW1cG3n67jvwHCToAAAAAIF9Upfb169fL1V26yJAHHpCUlBT9iPNU5fYpkyZJaGio7vEfJOgAAAAAgDzbsGGD3DlokFzYrJks+vFH3WtOrx495IrLL9eRfyFBBwAAAACck0OHDsnszz+Xzl26SKMLLpA333pL0tPT9aPmRFesKM9Mn64j/0OCDgAAAADIUVJSkqxbt07eevttib3uOqleq5Zdof37H36wl7e7ITw8XD547z2JiorSPf6HBB0AAAAAIJmZmXLq1Cl7dnzDxo3y1Zw5Muqxx+TyK6+UWnXq2EvYB955p8z55htjR6dlJzAwUB579FFp3bq17vFPJOgAAAAAUMidOHFCWrdpI42aNJEatWvL+Y0by3U9esjESZNk4aJFkpiYKBkZGfq/dl+fXr1k6JAhOvJfJOgAAAAAUMgVKVJEdu7aJdu2b7eXs3tJ2zZt5OWXXpKgoCDd479I0AEAAACgkFNHl7Vu1UpH3tH0wgvl01mzJCIiQvf4NxJ0AAAAAIDUOe883fKGi1u3lrnffCOlSpXSPf6PBB0AAAAAIM2aNdOtgqVm86/p0kW+njNHSpUsqXsLBxJ0AAAAAIBUqVxZtwqOSs6H3H+/fPjBB1IkMlL3Fh4k6AAAAAAAiYmJkWLFiunIfSVKlJB3Z8yQpyZOlJCQEN1buJCgAwAAAACkePHiEh4WpiP3BAYEyGWXXirLly6VXr166d7CiQQdAAAAAGDPWru9Dz26YkV56cUX5asvvrBn8As7EnQAAAAAgK1Bgwa6ZVZkRITcPWiQrFyxQm695ZZCccZ5bpCgAwAAAABsFzZpoltmhIeHyx0DB8rKv/6S6VOnSslCVqX9bEjQAQAAAAC2OnXq6JazqsbEyNjHH5fNGzfK888+K9WqVtWP4HQk6AAAAAAAW3R0tBQtUkRH+VPJ+rv6XX+9fD93rmxcv15GPPywlC9fXj+KMyFBBwAAAADYihYtKuXymESr/2+d886TO++4QxbNny8b1q2Tt958Uzp06MAe81wiQQcAAAAA2MLCwiSmShUdnVlAQIBd5E3tH29/ySVy3733ytyvv5YNa9faRd+ee+YZufjii+395jg3JOgAAAAAgP9q2aKF/We5smWlcePG0rFDB7kuNtZeoj5zxgyZP2+erLeS8V3x8TLvu+/k6cmT5dJOnezl68yU5w8JOgAAAADgv0Y9+qiknToluxISZNmSJfLd3Lny0Qcf2EXe+vTuLW3btLH3qoeGhur/B5xCgg4AAAAA+C8S74JDgg4AAAAAgAeQoAMAAAAA4AEk6AAAAAAAeAAJOgAAAAAAHuAjCXqA/b+zyTx1SrcA/5aZfFK3chDE/TcAAADAl/jECD4wNESCihXTUfaSVq3RLcC/nVy2XLeyFxJVRrcAAAAA+AKfmWILv6CxbmXv6NdzdQvwX1lpaXLsh/k6yl5o5WjdAgAAAOALfCZBjzy/oW5l78S8BZJ+6JCOAP907JdfJX3PPh1lL7JFM90CAAAA4At8JkEvenEr61+b80b0jKNHZfdTT4tkZuoewL9kJCfL3vGTRLKydM+ZBZUvJ+HVqukIAAAAgC/wmQQ9rFpVCa1eXUfZO/rp55L4yWc6AvxHVnq67Bo5WlI3b9U92SveqYMEBPrM2xsAAACAxWdG8IGhoVKqV3cd5SAjQ/aOfFwOvPOuZFltwB9kJCVJ/IMPy9Ev5uieHFiJeanePXQAAAAAwFf41BRbVN/eElSqpI6yp2Ya9z4+Trbffpec2rqNJe/wWeq1fOynX2TztT3lmErOz7K0XYlscZEUyUXNBgAAAADeEpBl0W3HqeRiU5dYSd0Yp3uyFzVooEQPG6qj7O1/5XXZN3GKjs4uIDhYIpo1laLt20lErRoSVLasfgTwqKxMSYvfJSc3bpTj3/8gKZs26wfOLiA0RGp8+oFENsw5QVerS+I6x0rKxk26J3slu3WVKtMm6wgAAACAKT6XoGempsnm2J6Ssm6D7gHwt1I3XS+Vxzymo+yRoAMAAADe43NVpAJDQ6TK1EkSWKSI7gGghNWvK9EjhusIAAAAgK8xm6AHBFj/y/lotP9KT9eNs4uoc55Umj5JAkJCdA9QuAVXKC/VXn9JAsPDdc9ZqHUzuVw8o7aJAAAAADDPeIIeGJm7me7cHB11upKdOkrF8WPsPbdAYRYUVVqqvf2ahFasqHvOListVTKOH9dRzoKrV9UtAAAAACYZTdDVOczBuai6rpzasUO3cslK/qN6XCdVXnlBAosX051A4RJap7bU+OR9e1XJuUg/dFjS9x/QUc5CKlTQLQAAAAAmGd+DHnZ+fd3KWdqWbZKyc6eOcq9E+3ZS68tPJKJ5U90D+D+17Lxk315S67OPJLxaNd2be8cX/y6SkaGjHAQESFiVyjoAAAAAYJLxBD3ivNzP7B3++DPdOjdhVatKzfffkehJ4ySkahXdC/ihoCCJaHahVP/4XakybowERUbqB3JPVXA//PEnOspZYES4hFU/9xsAAAAAAM6d0WPWlLTEg7KhRVuRzEzdk72QypXkvO/nWElBhO45d5kpKXLsx5/l4DvvyqnVayXzWO722QKepbaKRJWWyNYtpcyAmyWyXj0JsBL1vEpatVq2de9rH4N4NqE1qkudH76xZ9IBAAAAmGU8QVc2XdtDUlat0VHOyg65Vyrcd7eO8if9yBE5uXGTJC9fISmbNkv6sWOSlZqmHwW8KygyQoKKF5fwCxpLkSaNJaxaNbsvv1RSvqXvTXJy2XLdk7PSt/WXSo+O0BEAAAAAk1xJ0Pe9/Jrsf+ppHeUsMDJSqn/ynj1LCMBZB955T/Y+/mTujlgLCpSaX34qkfV5LwIAAABuML4HXSnZ+apcL8nNTE6W+Dvvy1PBOADZO7rwR9k3bmKuzz8Pq1VTIs6rrSMAAAAAprmSoKsq0EWvvkJHZ5cWnyBb+9woJzdv0T0A8sxKyI98+70k3HXvOW3xiLrlJrtaPAAAAAB3uJKgK+XuvP2cBvvpu/bI1tjecujzL3NVzArA/8o4cUJ2TZgkCfcMkayUVN17diHVqkqp2Gt1BAAAAMANriXokfXqSvHYrjrKnUyVXAx9SLbccLOcWLqMRB3IpcxTp+Tgp7Ml7oqucui1t3J35vnfAgKk3H2DJDA0VHcAAAAAcIMrReL+lnYgUeI6d5MM689zZiUNoTVrSNF2F0uRC5rY7aASJfSDQCGXlSnp+w/IqU1xkrRkqZz4dbFkWHFeFLHeY9Xfek0CAl27fwcAAADA4mqCrhz5YYG9F1bSz2FGLzuczQz8PwfeyoElikutObMlrHIl3QMAAADALa4n6CqJ2DPpaUl8+XXdAcALAsJCJebVF6V4uza6BwAAAICb3F/DGhAgFR4cIiWuowAV4BlBgVJh9EiScwAAAKAAFcgmU3UmeuVxY6TopR10D4ACExgo5R4YLGX69NIdAAAAAAqC+0vcT5OVmio7HxsrRz76RPcAcJNa1l5xzCiJ6t1T9wAAAAAoKAWaoNusb5/43geyb8IUyUxO1p0ATAuJqSyVp0yUos0u0j0AAAAAClLBJ+jaqe3bZeeDI+Tk8hVW0q47ATguIDRUSlzbRaIfe0SCihbVvQAAAAAKmmcSdCUrPV2OfP+D7Js8TdJ2xNuz6wCcERASLBFNGttL2iPr1rE6OKYQAAAA8BJPJeh/y0xJkeO/LpbEN96Wk38ssxN3AHlgJeGBRSKl2GWdpMytN0lk/fp2UTgAAAAA3uPJBP10qfv3y/FFP0nSb7/LyY2bJG3LNslKS9OPAvi3ACshD6tVUyIbny9F27aRoq1aSFCRIvpRAAAAAF7l+QT9H6x/qppNTzt8RDJOHJf0g4ckKzNTPwgUXoHhYRJUvIQElywpwSWKSwCz5AAAAIDP8a0EHQAAAAAAP8U0GwAAAAAAHkCCDgAAAACAB5CgAwAAAADgASToAAAAAAB4AAk6AAAAAAAeQIIOAAAAAIAHkKADAAAAAOABJOgAAAAAAHgACToAAAAAAB5Agg4AAAAAgAeQoAMAAAAA4AEk6AAAAAAAeAAJOgAAAAAAHkCCDgAAAACAB5CgAwAAAADgASToAAAAAAB4AAk6AAAAAAAFTuT/AEi4PhsWDpChAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"cdbdaea2-c415-5073-50f7-c04e968640b6\": {\n    \"name\": \"Excelsecu eSecu FIDO2 Security Key\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAYCAYAAAAoNxVrAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\"\n  },\n  \"bc2fe499-0d8e-4ffe-96f3-94a82840cf8c\": {\n    \"name\": \"OCTATCO EzQuant FIDO2 AUTHENTICATOR\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"eb3b131e-59dc-536a-d176-cb7306da10f5\": {\n    \"name\": \"ellipticSecure MIRkey USB Authenticator\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"1c086528-58d5-f211-823c-356786e36140\": {\n    \"name\": \"Atos CardOS FIDO2\",\n    \"icon_light\": \"data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAKAEGACtYuHw7fWlJAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAKAEGACtYuHw7fWlJAAAAAElFTkSuQmCC\"\n  },\n  \"77010bd7-212a-4fc9-b236-d2ca5e9d4084\": {\n    \"name\": \"Feitian BioPass FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"d94a29d9-52dd-4247-9c2d-8b818b610389\": {\n    \"name\": \"VeriMark Guard Fingerprint Key\",\n    \"icon_light\": \"data:image/png;base64,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"833b721a-ff5f-4d00-bb2e-bdda3ec01e29\": {\n    \"name\": \"Feitian ePass FIDO2 Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAUCAMAAAAtBkrlAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE0IChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxNi0xMi0zMFQxNDozMzowOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTYtMTItMzBUMDc6MzE6NTkrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6SGlzdG9yeT0iMjAxNi0xMi0zMFQxNTozMDoyNyswODowMCYjeDk75paH5Lu2IOacquagh+mimC0xIOW3suaJk+W8gCYjeEE7IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJFNzFCRkZDQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJFNzFCRkZEQzY3RjExRTY5NzhEQTlDQkI2NDYzRjkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkU3MUJGRkFDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkU3MUJGRkJDNjdGMTFFNjk3OERBOUNCQjY0NjNGOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz477JXFAAAAYFBMVEX///8EVqIXZavG2OoqcLG2zOOkwt0BSJtqlcXV4u+autlWhbzk7PUAMY9HcrKjtNbq8feAl8aBoszz9vpdjsGGqtF3n8uTsNSZpc6JsNT5+v0xYKnu8Pff5/L48fg/friczJgYAAADAElEQVR42kRUCZbDIAjFXZOY1TatNc39bzksSYc3r4ME4fMBAaD6zl8y/9TOget8d5jfN78bwM/dDCRpR521zXfojHJ05IIyhBAUSVAONdGzBYt2f7KFrfkJaAkHh9FZhcDXHRkTKo9MLihGaavImnV3qyEX0Eprgz/4DwUD7kCHRnd8QFN43Go4UVmDDgza4w27oizdA2+cK+uuUpjjo2+xwc/42W50x5LGYeDBsR0HVIx5x8iF60CblbTEEkFr27bNDBUVSq1OKVPbE62b3EH8FqBg5OOOEuc2t8ZJiqMOuGp+cKjg7wVGceozqN4pxgVPQkjFYgbVJKDUhDCjYrawP5q4ETgC9fIMRHtitpQcCvJOELcbMsQgnciRkljpyQjvG44jqBUETFiBi1PEIyekOzsW+Ty5cLHos5R+dMS1LtSSxf3gQHczR2CI4gMNpW4IRA1QMa6tJ4+C6uHuGE8mNDIyFqg/OP/MMUueS6Iq8S90dAeBJSEy/qKkK+BNwz8cYY4jb5J6u4iWCI2B1Z56LW5kEc4hkdMpsvUC5585SX0QubcgNqyfgDFEcTt+40/0S5Nx0waCw3OKkcObA5In0AYp01pjjw2n626UDjtHwa28iHuTKqtrv+reW41NZ6iGlr7uuLJCfkFtctcG04sgm1eNS+ZaDnpaTErGoyX5JK2iMz8xs0nOwWGcPDN49qaCd4bzJozDZm/aBK+EozLw+XhNBiYwHf0siOu1XPkG/zKwvqYKcfSwDEcH/oUe07es/WQ8rIyg2DOXj8tjkZduDB/b8hzDllMMOCS5BEnd534f8ti3UZc4kMs3xLyafMSsJhdG8XPqjNk5tAgO25feKChnVdDj/J0FMkOsU/xMBv0wFhYeEGfVH13fuDU0yDFLa4fc7RnWHBfuTFV2tEmNwadc7ac3UY2jfBl7HT36fe34iQO5mNCFFBW07KjPgqhOLU01vZ8PueZ2JClFZN8jkUs69uka9ePp6+EfL4AF5+NywSbirHtcB8Ml/gkwAEjkK64KjHPeAAAAAElFTkSuQmCC\"\n  },\n  \"ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4\": {\n    \"name\": \"Google Password Manager\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE5MiAxOTIiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDE5MiAxOTIiIHdpZHRoPSIyNHB4Ij48cmVjdCBmaWxsPSJub25lIiBoZWlnaHQ9IjE5MiIgd2lkdGg9IjE5MiIgeT0iMCIvPjxnPjxwYXRoIGQ9Ik02OS4yOSwxMDZjLTMuNDYsNS45Ny05LjkxLDEwLTE3LjI5LDEwYy0xMS4wMywwLTIwLTguOTctMjAtMjBzOC45Ny0yMCwyMC0yMCBjNy4zOCwwLDEzLjgzLDQuMDMsMTcuMjksMTBoMjUuNTVDOTAuMyw2Ni41NCw3Mi44Miw1Miw1Miw1MkMyNy43NCw1Miw4LDcxLjc0LDgsOTZzMTkuNzQsNDQsNDQsNDRjMjAuODIsMCwzOC4zLTE0LjU0LDQyLjg0LTM0IEg2OS4yOXoiIGZpbGw9IiM0Mjg1RjQiLz48cmVjdCBmaWxsPSIjRkJCQzA0IiBoZWlnaHQ9IjI0IiB3aWR0aD0iNDQiIHg9Ijk0IiB5PSI4NCIvPjxwYXRoIGQ9Ik05NC4zMiw4NEg2OHYwLjA1YzIuNSwzLjM0LDQsNy40Nyw0LDExLjk1cy0xLjUsOC42MS00LDExLjk1VjEwOGgyNi4zMiBjMS4wOC0zLjgyLDEuNjgtNy44NCwxLjY4LTEyUzk1LjQxLDg3LjgyLDk0LjMyLDg0eiIgZmlsbD0iI0VBNDMzNSIvPjxwYXRoIGQ9Ik0xODQsMTA2djI2aC0xNnYtOGMwLTQuNDItMy41OC04LTgtOHMtOCwzLjU4LTgsOHY4aC0xNnYtMjZIMTg0eiIgZmlsbD0iIzM0QTg1MyIvPjxyZWN0IGZpbGw9IiMxODgwMzgiIGhlaWdodD0iMjQiIHdpZHRoPSI0OCIgeD0iMTM2IiB5PSI4NCIvPjwvZz48L3N2Zz4=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE5MiAxOTIiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDE5MiAxOTIiIHdpZHRoPSIyNHB4Ij48cmVjdCBmaWxsPSJub25lIiBoZWlnaHQ9IjE5MiIgd2lkdGg9IjE5MiIgeT0iMCIvPjxnPjxwYXRoIGQ9Ik02OS4yOSwxMDZjLTMuNDYsNS45Ny05LjkxLDEwLTE3LjI5LDEwYy0xMS4wMywwLTIwLTguOTctMjAtMjBzOC45Ny0yMCwyMC0yMCBjNy4zOCwwLDEzLjgzLDQuMDMsMTcuMjksMTBoMjUuNTVDOTAuMyw2Ni41NCw3Mi44Miw1Miw1Miw1MkMyNy43NCw1Miw4LDcxLjc0LDgsOTZzMTkuNzQsNDQsNDQsNDRjMjAuODIsMCwzOC4zLTE0LjU0LDQyLjg0LTM0IEg2OS4yOXoiIGZpbGw9IiM0Mjg1RjQiLz48cmVjdCBmaWxsPSIjRkJCQzA0IiBoZWlnaHQ9IjI0IiB3aWR0aD0iNDQiIHg9Ijk0IiB5PSI4NCIvPjxwYXRoIGQ9Ik05NC4zMiw4NEg2OHYwLjA1YzIuNSwzLjM0LDQsNy40Nyw0LDExLjk1cy0xLjUsOC42MS00LDExLjk1VjEwOGgyNi4zMiBjMS4wOC0zLjgyLDEuNjgtNy44NCwxLjY4LTEyUzk1LjQxLDg3LjgyLDk0LjMyLDg0eiIgZmlsbD0iI0VBNDMzNSIvPjxwYXRoIGQ9Ik0xODQsMTA2djI2aC0xNnYtOGMwLTQuNDItMy41OC04LTgtOHMtOCwzLjU4LTgsOHY4aC0xNnYtMjZIMTg0eiIgZmlsbD0iIzM0QTg1MyIvPjxyZWN0IGZpbGw9IiMxODgwMzgiIGhlaWdodD0iMjQiIHdpZHRoPSI0OCIgeD0iMTM2IiB5PSI4NCIvPjwvZz48L3N2Zz4=\"\n  },\n  \"adce0002-35bc-c60a-648b-0b25f1f05503\": {\n    \"name\": \"Chrome on Mac\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDggNDgiPgogIDxkZWZzPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMy4yMTczIiB5MT0iMTUiIHgyPSI0NC43ODEyIiB5Mj0iMTUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZDkzMDI1Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2VhNDMzNSIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjIwLjcyMTkiIHkxPSI0Ny42NzkxIiB4Mj0iNDEuNTAzOSIgeTI9IjExLjY4MzciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmNjOTM0Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZiYmMwNCIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjI2LjU5ODEiIHkxPSI0Ni41MDE1IiB4Mj0iNS44MTYxIiB5Mj0iMTAuNTA2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzFlOGUzZSIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNGE4NTMiLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAKICAgIDxwYXRoIGlkPSJwIiBkPSJNMTMuNjA4NiAzMC4wMDMxIDMuMjE4IDEyLjAwNkEyMy45OTQgMjMuOTk0IDAgMCAwIDI0LjAwMjUgNDhsMTAuMzkwNi0xNy45OTcxLS4wMDY3LS4wMDY4YTExLjk4NTIgMTEuOTg1MiAwIDAgMS0yMC43Nzc4LjAwN1oiLz4KICA8L2RlZnM+CiAgCiAgPHVzZSB4bGluazpocmVmPSIjcCIgZmlsbD0idXJsKCNhKSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDI0IDI0KSIvPgogIDx1c2UgeGxpbms6aHJlZj0iI3AiIGZpbGw9InVybCgjYikiIHRyYW5zZm9ybT0icm90YXRlKC0xMjAgMjQgMjQpIi8+CiAgPHVzZSB4bGluazpocmVmPSIjcCIgZmlsbD0idXJsKCNjKSIvPgogIAogIDxjaXJjbGUgY3g9IjI0IiBjeT0iMjQiIHI9IjEyIiBzdHlsZT0iZmlsbDojZmZmIi8+CiAgPGNpcmNsZSBjeD0iMjQiIGN5PSIyNCIgcj0iOS41IiBzdHlsZT0iZmlsbDojMWE3M2U4Ii8+Cjwvc3ZnPg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDggNDgiPgogIDxkZWZzPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMy4yMTczIiB5MT0iMTUiIHgyPSI0NC43ODEyIiB5Mj0iMTUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZDkzMDI1Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2VhNDMzNSIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjIwLjcyMTkiIHkxPSI0Ny42NzkxIiB4Mj0iNDEuNTAzOSIgeTI9IjExLjY4MzciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmNjOTM0Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZiYmMwNCIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjI2LjU5ODEiIHkxPSI0Ni41MDE1IiB4Mj0iNS44MTYxIiB5Mj0iMTAuNTA2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzFlOGUzZSIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNGE4NTMiLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAKICAgIDxwYXRoIGlkPSJwIiBkPSJNMTMuNjA4NiAzMC4wMDMxIDMuMjE4IDEyLjAwNkEyMy45OTQgMjMuOTk0IDAgMCAwIDI0LjAwMjUgNDhsMTAuMzkwNi0xNy45OTcxLS4wMDY3LS4wMDY4YTExLjk4NTIgMTEuOTg1MiAwIDAgMS0yMC43Nzc4LjAwN1oiLz4KICA8L2RlZnM+CiAgCiAgPHVzZSB4bGluazpocmVmPSIjcCIgZmlsbD0idXJsKCNhKSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDI0IDI0KSIvPgogIDx1c2UgeGxpbms6aHJlZj0iI3AiIGZpbGw9InVybCgjYikiIHRyYW5zZm9ybT0icm90YXRlKC0xMjAgMjQgMjQpIi8+CiAgPHVzZSB4bGluazpocmVmPSIjcCIgZmlsbD0idXJsKCNjKSIvPgogIAogIDxjaXJjbGUgY3g9IjI0IiBjeT0iMjQiIHI9IjEyIiBzdHlsZT0iZmlsbDojZmZmIi8+CiAgPGNpcmNsZSBjeD0iMjQiIGN5PSIyNCIgcj0iOS41IiBzdHlsZT0iZmlsbDojMWE3M2U4Ii8+Cjwvc3ZnPg==\"\n  },\n  \"dd4ec289-e01d-41c9-bb89-70fa845d4bf2\": {\n    \"name\": \"iCloud Keychain (Managed)\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJub25lIj48cGF0aCBkPSJtMjE3LjM2LDkwLjY5Yy0xNS41OCw5LjU0LTI1LjE3LDI2LjQxLTI1LjM4LDQ0LjY4LjA2LDIwLjY3LDEyLjQzLDM5LjMyLDMxLjQ2LDQ3LjQxLTMuNjcsMTEuODQtOS4xLDIzLjA2LTE2LjExLDMzLjI4LTEwLjAzLDE0LjQ0LTIwLjUyLDI4Ljg3LTM2LjQ3LDI4Ljg3cy0yMC4wNi05LjI3LTM4LjQ1LTkuMjctMjQuMzIsOS41Ny0zOC45LDkuNTctMjQuNzctMTMuMzctMzYuNDctMjkuNzljLTE1LjQ2LTIyLjk5LTIzLjk1LTQ5Ljk2LTI0LjQ3LTc3LjY2LDAtNDUuNTksMjkuNjMtNjkuNzUsNTguODEtNjkuNzUsMTUuNSwwLDI4LjQyLDEwLjE4LDM4LjE1LDEwLjE4czIzLjcxLTEwLjc5LDQxLjM0LTEwLjc5YzE4LjQxLS40NywzNS44NCw4LjI0LDQ2LjUsMjMuMjVabS01NC44Ni00Mi41NWM3Ljc3LTkuMTQsMTIuMTctMjAuNjcsMTIuNDYtMzIuNjcuMDEtMS41OC0uMTQtMy4xNi0uNDYtNC43MS0xMy4zNSwxLjMtMjUuNjksNy42Ny0zNC41LDE3Ljc4LTcuODUsOC43OC0xMi40MSwyMC0xMi45MiwzMS43NiwwLDEuNDMuMTYsMi44Ni40Niw0LjI2LDEuMDUuMiwyLjEyLjMsMy4xOS4zLDEyLjQzLS45OSwyMy45MS03LjA0LDMxLjc2LTE2LjczWiIgZmlsbD0iI0ZGRiIvPjwvc3ZnPg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJub25lIj48cGF0aCBkPSJtMjE3LjM2LDkwLjY5Yy0xNS41OCw5LjU0LTI1LjE3LDI2LjQxLTI1LjM4LDQ0LjY4LjA2LDIwLjY3LDEyLjQzLDM5LjMyLDMxLjQ2LDQ3LjQxLTMuNjcsMTEuODQtOS4xLDIzLjA2LTE2LjExLDMzLjI4LTEwLjAzLDE0LjQ0LTIwLjUyLDI4Ljg3LTM2LjQ3LDI4Ljg3cy0yMC4wNi05LjI3LTM4LjQ1LTkuMjctMjQuMzIsOS41Ny0zOC45LDkuNTctMjQuNzctMTMuMzctMzYuNDctMjkuNzljLTE1LjQ2LTIyLjk5LTIzLjk1LTQ5Ljk2LTI0LjQ3LTc3LjY2LDAtNDUuNTksMjkuNjMtNjkuNzUsNTguODEtNjkuNzUsMTUuNSwwLDI4LjQyLDEwLjE4LDM4LjE1LDEwLjE4czIzLjcxLTEwLjc5LDQxLjM0LTEwLjc5YzE4LjQxLS40NywzNS44NCw4LjI0LDQ2LjUsMjMuMjVabS01NC44Ni00Mi41NWM3Ljc3LTkuMTQsMTIuMTctMjAuNjcsMTIuNDYtMzIuNjcuMDEtMS41OC0uMTQtMy4xNi0uNDYtNC43MS0xMy4zNSwxLjMtMjUuNjksNy42Ny0zNC41LDE3Ljc4LTcuODUsOC43OC0xMi40MSwyMC0xMi45MiwzMS43NiwwLDEuNDMuMTYsMi44Ni40Niw0LjI2LDEuMDUuMiwyLjEyLjMsMy4xOS4zLDEyLjQzLS45OSwyMy45MS03LjA0LDMxLjc2LTE2LjczWiIgZmlsbD0iIzAwMCIvPjwvc3ZnPg==\"\n  },\n  \"531126d6-e717-415c-9320-3d9aa6981239\": {\n    \"name\": \"Dashlane\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yNTcuNDc0IDM1OS4yMDlDMjU3LjQ3NCAzNTYuMTg5IDI1NC40NTQgMzUzLjE2OSAyNTAuMjE1IDM1MS45NTlMMTk5LjQxMSAzMzMuMjMxQzE5MC44OTUgMzI5LjYwMSAxODEuMjY0IDMzMy44MzEgMTgxLjI2NCAzMzkuODlWNDc1Ljc3OUMxODEuMjY0IDQ3OC44MDkgMTg0LjI4MyA0ODIuNDM4IDE4Ny4zMDMgNDgzLjY0OEwyMzkuMzI2IDUwMi4zNzZDMjQ3LjE5NSA1MDUuMzk2IDI1Ny40NzQgNTAxLjE2NiAyNTcuNDc0IDQ5NC41MDhWMzU5LjIwOVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0zNTIuNzM2IDMyMS4xMDRDMzUyLjczNiAzMTguMDg0IDM0OS43MTcgMzE1LjA2NCAzNDUuNDc3IDMxMy44NTRMMjk0LjY3NCAyOTUuMTI2QzI4Ni4xNTcgMjkxLjQ5NiAyNzYuNTI2IDI5NS43MjYgMjc2LjUyNiAzMDEuNzg1VjQzNy42NzRDMjc2LjUyNiA0NDAuNzA0IDI3OS41NDYgNDQ0LjMzMyAyODIuNTY2IDQ0NS41NDNMMzM0LjU4OSA0NjQuMjcxQzM0Mi40NTggNDY3LjI5MSAzNTIuNzM2IDQ2My4wNjEgMzUyLjczNiA0NTYuNDAzVjMyMS4xMDRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjU3LjQ3NCAzNS4zMjExQzI1Ny40NzQgMzIuMzAxMyAyNTQuNDU0IDI5LjI4MTUgMjUwLjIxNSAyOC4wNzE3TDE5OS40MTEgOS4zNDM0M0MxOTAuODk1IDUuNzEzOTkgMTgxLjI2NCA5Ljk0MzU4IDE4MS4yNjQgMTYuMDAyMlYxNTEuODkyQzE4MS4yNjQgMTU0LjkyMSAxODQuMjgzIDE1OC41NTEgMTg3LjMwMyAxNTkuNzZMMjM5LjMyNiAxNzguNDg5QzI0Ny4xOTUgMTgxLjUwOSAyNTcuNDc0IDE3Ny4yNzkgMjU3LjQ3NCAxNzAuNjJWMzUuMzIxMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0zNTIuNzM2IDkyLjQ3NzdDMzUyLjczNiA4OS40NTc5IDM0OS43MTcgODYuNDM4MiAzNDUuNDc3IDg1LjIyODNMMjk0LjY3NCA2Ni41QzI4Ni4xNTcgNjIuODcwNiAyNzYuNTI2IDY3LjEwMDIgMjc2LjUyNiA3My4xNTg4VjIwOS4wNDhDMjc2LjUyNiAyMTIuMDc4IDI3OS41NDYgMjE1LjcwNyAyODIuNTY2IDIxNi45MTdMMzM0LjU4OSAyMzUuNjQ1QzM0Mi40NTggMjM4LjY2NSAzNTIuNzM2IDIzNC40MzYgMzUyLjczNiAyMjcuNzc3VjkyLjQ3NzdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNDQ4IDE2OC42ODdDNDQ4IDE2NS42NjcgNDQ0Ljk4IDE2Mi42NDcgNDQwLjc0MSAxNjEuNDM3TDM4OS45MzcgMTQyLjcwOUMzODEuNDIxIDEzOS4wNzkgMzcxLjc5IDE0My4zMDkgMzcxLjc5IDE0OS4zNjhWMzYxLjQ2NkMzNzEuNzkgMzY0LjQ5NSAzNzQuODEgMzY4LjEyNSAzNzcuODI5IDM2OS4zMzVMNDI5Ljg1MiAzODguMDYzQzQzNy43MjEgMzkxLjA4MyA0NDggMzg2Ljg1MyA0NDggMzgwLjE5NFYxNjguNjg3WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE2Mi4yMSAzNS4zMzA2QzE2Mi4yMSAzMi4zMTA4IDE1OS4xOSAyOS4yODE1IDE1NC45NTEgMjguMDcxN0wxMDQuMTQ4IDkuMzQzNDNDOTUuNjc4NyA1LjcxMzk5IDg2IDkuOTQzNTggODYgMTYuMDAyMlY0NzUuNzg5Qzg2IDQ3OC44MDggODkuMDE5OCA0ODIuNDM4IDkyLjA0OTIgNDgzLjY0OEwxNDQuMDYzIDUwMi4zNzZDMTUxLjkzMSA1MDUuMzk2IDE2Mi4yMSA1MDEuMTY2IDE2Mi4yMSA0OTQuNTA3VjM1LjMzMDZaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yNTcuNDc0IDM1OS4yMDlDMjU3LjQ3NCAzNTYuMTg5IDI1NC40NTQgMzUzLjE2OSAyNTAuMjE1IDM1MS45NTlMMTk5LjQxMSAzMzMuMjMxQzE5MC44OTUgMzI5LjYwMSAxODEuMjY0IDMzMy44MzEgMTgxLjI2NCAzMzkuODlWNDc1Ljc3OUMxODEuMjY0IDQ3OC44MDkgMTg0LjI4MyA0ODIuNDM4IDE4Ny4zMDMgNDgzLjY0OEwyMzkuMzI2IDUwMi4zNzZDMjQ3LjE5NSA1MDUuMzk2IDI1Ny40NzQgNTAxLjE2NiAyNTcuNDc0IDQ5NC41MDhWMzU5LjIwOVoiIGZpbGw9IiMwOTM2M0YiLz4KPHBhdGggZD0iTTM1Mi43MzYgMzIxLjEwM0MzNTIuNzM2IDMxOC4wODQgMzQ5LjcxNyAzMTUuMDY0IDM0NS40NzcgMzEzLjg1NEwyOTQuNjc0IDI5NS4xMjZDMjg2LjE1NyAyOTEuNDk2IDI3Ni41MjYgMjk1LjcyNiAyNzYuNTI2IDMwMS43ODVWNDM3LjY3NEMyNzYuNTI2IDQ0MC43MDQgMjc5LjU0NiA0NDQuMzMzIDI4Mi41NjYgNDQ1LjU0M0wzMzQuNTg5IDQ2NC4yNzFDMzQyLjQ1OCA0NjcuMjkxIDM1Mi43MzYgNDYzLjA2MSAzNTIuNzM2IDQ1Ni40MDNWMzIxLjEwM1oiIGZpbGw9IiMwOTM2M0YiLz4KPHBhdGggZD0iTTI1Ny40NzQgMzUuMzIxMUMyNTcuNDc0IDMyLjMwMTMgMjU0LjQ1NCAyOS4yODE1IDI1MC4yMTUgMjguMDcxN0wxOTkuNDExIDkuMzQzNDNDMTkwLjg5NSA1LjcxMzk5IDE4MS4yNjQgOS45NDM1OCAxODEuMjY0IDE2LjAwMjJWMTUxLjg5MkMxODEuMjY0IDE1NC45MjEgMTg0LjI4MyAxNTguNTUxIDE4Ny4zMDMgMTU5Ljc2TDIzOS4zMjYgMTc4LjQ4OUMyNDcuMTk1IDE4MS41MDggMjU3LjQ3NCAxNzcuMjc5IDI1Ny40NzQgMTcwLjYyVjM1LjMyMTFaIiBmaWxsPSIjMDkzNjNGIi8+CjxwYXRoIGQ9Ik0zNTIuNzM2IDkyLjQ3NzdDMzUyLjczNiA4OS40NTc5IDM0OS43MTcgODYuNDM4MiAzNDUuNDc3IDg1LjIyODNMMjk0LjY3NCA2Ni41QzI4Ni4xNTcgNjIuODcwNiAyNzYuNTI2IDY3LjEwMDIgMjc2LjUyNiA3My4xNTg4VjIwOS4wNDhDMjc2LjUyNiAyMTIuMDc4IDI3OS41NDYgMjE1LjcwNyAyODIuNTY2IDIxNi45MTdMMzM0LjU4OSAyMzUuNjQ1QzM0Mi40NTggMjM4LjY2NSAzNTIuNzM2IDIzNC40MzYgMzUyLjczNiAyMjcuNzc3VjkyLjQ3NzdaIiBmaWxsPSIjMDkzNjNGIi8+CjxwYXRoIGQ9Ik00NDggMTY4LjY4N0M0NDggMTY1LjY2NyA0NDQuOTggMTYyLjY0NyA0NDAuNzQxIDE2MS40MzdMMzg5LjkzNyAxNDIuNzA5QzM4MS40MjEgMTM5LjA3OSAzNzEuNzkgMTQzLjMwOSAzNzEuNzkgMTQ5LjM2OFYzNjEuNDY2QzM3MS43OSAzNjQuNDk1IDM3NC44MSAzNjguMTI1IDM3Ny44MjkgMzY5LjMzNUw0MjkuODUyIDM4OC4wNjNDNDM3LjcyMSAzOTEuMDgzIDQ0OCAzODYuODUzIDQ0OCAzODAuMTk0VjE2OC42ODdaIiBmaWxsPSIjMDkzNjNGIi8+CjxwYXRoIGQ9Ik0xNjIuMjEgMzUuMzMwNkMxNjIuMjEgMzIuMzEwOCAxNTkuMTkgMjkuMjgxNSAxNTQuOTUxIDI4LjA3MTdMMTA0LjE0OCA5LjM0MzQzQzk1LjY3ODcgNS43MTM5OSA4NiA5Ljk0MzU4IDg2IDE2LjAwMjJWNDc1Ljc4OUM4NiA0NzguODA4IDg5LjAxOTggNDgyLjQzOCA5Mi4wNDkyIDQ4My42NDhMMTQ0LjA2MyA1MDIuMzc2QzE1MS45MzEgNTA1LjM5NiAxNjIuMjEgNTAxLjE2NiAxNjIuMjEgNDk0LjUwN1YzNS4zMzA2WiIgZmlsbD0iIzA5MzYzRiIvPgo8L3N2Zz4K\"\n  },\n  \"bada5566-a7aa-401f-bd96-45619a55120d\": {\n    \"name\": \"1Password\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQwIiBoZWlnaHQ9IjI0MCIgdmlld0JveD0iMCAwIDI0MCAyNDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjM5LjI1NyAxMjAuNDE3QzIzOS4yNTcgNTQuNDE5MyAxODUuNzU1IDAuOTE2NTA0IDExOS43NTcgMC45MTY1MDRDNTMuNzYwMSAwLjkxNjUwNCAwLjI1NzMyNCA1NC40MTkzIDAuMjU3MzI0IDEyMC40MTdDMC4yNTczMjQgMTg2LjQxNyA1My43NjAxIDIzOS45MTcgMTE5Ljc1NyAyMzkuOTE3QzE4NS43NTUgMjM5LjkxNyAyMzkuMjU3IDE4Ni40MTcgMjM5LjI1NyAxMjAuNDE3Wk05OC4wMDY5IDU0LjAyNzZDOTcuMDY3NCA1NS44NzE0IDk3LjA2NzQgNTguMjg1MSA5Ny4wNjc0IDYzLjExMjZWOTAuNDcyOUM5Ny4wNjc0IDkxLjY3ODggOTcuMDY3NCA5Mi4yODE3IDk3LjIxOTYgOTIuODM5MkM5Ny4zNTQ1IDkzLjMzMzEgOTcuNTc2MyA5My43OTkgOTcuODc0NiA5NC4yMTVDOTguMjExMyA5NC42ODQ3IDk4LjY3OTIgOTUuMDY0OCA5OS42MTUyIDk1LjgyNTFMMTA2LjUzNiAxMDEuNDQ3QzEwNy42NjQgMTAyLjM2NCAxMDguMjI4IDEwMi44MjIgMTA4LjQzMyAxMDMuMzc0QzEwOC42MTMgMTAzLjg1NyAxMDguNjEzIDEwNC4zOSAxMDguNDMzIDEwNC44NzNDMTA4LjIyOCAxMDUuNDI1IDEwNy42NjQgMTA1Ljg4MyAxMDYuNTM2IDEwNi44TDk5LjYxNTIgMTEyLjQyMkM5OC42NzkzIDExMy4xODIgOTguMjExMyAxMTMuNTYyIDk3Ljg3NDYgMTE0LjAzMkM5Ny41NzYzIDExNC40NDggOTcuMzU0NSAxMTQuOTE0IDk3LjIxOTYgMTE1LjQwOEM5Ny4wNjc0IDExNS45NjUgOTcuMDY3NCAxMTYuNTY4IDk3LjA2NzQgMTE3Ljc3NFYxNzcuNzE5Qzk3LjA2NzQgMTgyLjU0NyA5Ny4wNjc0IDE4NC45NjEgOTguMDA2OSAxODYuODA1Qzk4LjgzMzMgMTg4LjQyNiAxMDAuMTUyIDE4OS43NDUgMTAxLjc3NCAxOTAuNTcxQzEwMy42MTggMTkxLjUxMSAxMDYuMDMxIDE5MS41MTEgMTEwLjg1OSAxOTEuNTExSDEyOC42NTZDMTMzLjQ4MyAxOTEuNTExIDEzNS44OTcgMTkxLjUxMSAxMzcuNzQxIDE5MC41NzFDMTM5LjM2MyAxODkuNzQ1IDE0MC42ODEgMTg4LjQyNiAxNDEuNTA4IDE4Ni44MDVDMTQyLjQ0NyAxODQuOTYxIDE0Mi40NDcgMTgyLjU0NyAxNDIuNDQ3IDE3Ny43MTlWMTUwLjM1OUMxNDIuNDQ3IDE0OS4xNTMgMTQyLjQ0NyAxNDguNTUgMTQyLjI5NSAxNDcuOTkzQzE0Mi4xNiAxNDcuNDk5IDE0MS45MzggMTQ3LjAzMyAxNDEuNjQgMTQ2LjYxN0MxNDEuMzAzIDE0Ni4xNDcgMTQwLjgzNSAxNDUuNzY3IDEzOS44OTkgMTQ1LjAwN0wxMzIuOTc4IDEzOS4zODVDMTMxLjg1IDEzOC40NjggMTMxLjI4NiAxMzguMDEgMTMxLjA4MiAxMzcuNDU5QzEzMC45MDIgMTM2Ljk3NSAxMzAuOTAyIDEzNi40NDMgMTMxLjA4MiAxMzUuOTU5QzEzMS4yODYgMTM1LjQwNyAxMzEuODUgMTM0Ljk0OSAxMzIuOTc4IDEzNC4wMzNMMTM5Ljg5OSAxMjguNDFDMTQwLjgzNSAxMjcuNjUgMTQxLjMwMyAxMjcuMjcgMTQxLjY0IDEyNi44QzE0MS45MzggMTI2LjM4NCAxNDIuMTYgMTI1LjkxOCAxNDIuMjk1IDEyNS40MjRDMTQyLjQ0NyAxMjQuODY3IDE0Mi40NDcgMTI0LjI2NCAxNDIuNDQ3IDEyMy4wNThWNjMuMTEyNkMxNDIuNDQ3IDU4LjI4NTEgMTQyLjQ0NyA1NS44NzE0IDE0MS41MDggNTQuMDI3NkMxNDAuNjgxIDUyLjQwNTcgMTM5LjM2MyA1MS4wODcgMTM3Ljc0MSA1MC4yNjA2QzEzNS44OTcgNDkuMzIxMSAxMzMuNDgzIDQ5LjMyMTEgMTI4LjY1NiA0OS4zMjExSDExMC44NTlDMTA2LjAzMSA0OS4zMjExIDEwMy42MTggNDkuMzIxMSAxMDEuNzc0IDUwLjI2MDZDMTAwLjE1MiA1MS4wODcgOTguODMzMyA1Mi40MDU3IDk4LjAwNjkgNTQuMDI3NloiIGZpbGw9IiNGRkZFRkIiLz4KPC9zdmc+Cg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQwIiBoZWlnaHQ9IjI0MCIgdmlld0JveD0iMCAwIDI0MCAyNDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjM5LjExNiAxMjAuNDE3QzIzOS4xMTYgNTQuNDE5MyAxODUuNjEzIDAuOTE2NTA0IDExOS42MTYgMC45MTY1MDRDNTMuNjE5IDAuOTE2NTA0IDAuMTE2MjExIDU0LjQxOTMgMC4xMTYyMTEgMTIwLjQxN0MwLjExNjIxMSAxODYuNDE3IDUzLjYxOSAyMzkuOTE3IDExOS42MTYgMjM5LjkxN0MxODUuNjEzIDIzOS45MTcgMjM5LjExNiAxODYuNDE3IDIzOS4xMTYgMTIwLjQxN1pNOTcuODY1OCA1NC4wMjc2Qzk2LjkyNjMgNTUuODcxNCA5Ni45MjYzIDU4LjI4NTEgOTYuOTI2MyA2My4xMTI2VjkwLjQ3MjlDOTYuOTI2MyA5MS42Nzg4IDk2LjkyNjMgOTIuMjgxNyA5Ny4wNzg1IDkyLjgzOTJDOTcuMjEzNCA5My4zMzMxIDk3LjQzNTIgOTMuNzk5IDk3LjczMzUgOTQuMjE1Qzk4LjA3MDIgOTQuNjg0NyA5OC41MzgxIDk1LjA2NDggOTkuNDc0MSA5NS44MjUxTDEwNi4zOTUgMTAxLjQ0N0MxMDcuNTIzIDEwMi4zNjQgMTA4LjA4NyAxMDIuODIyIDEwOC4yOTIgMTAzLjM3NEMxMDguNDcxIDEwMy44NTcgMTA4LjQ3MSAxMDQuMzkgMTA4LjI5MiAxMDQuODczQzEwOC4wODcgMTA1LjQyNSAxMDcuNTIzIDEwNS44ODMgMTA2LjM5NSAxMDYuOEw5OS40NzQxIDExMi40MjJDOTguNTM4MiAxMTMuMTgyIDk4LjA3MDIgMTEzLjU2MiA5Ny43MzM1IDExNC4wMzJDOTcuNDM1MiAxMTQuNDQ4IDk3LjIxMzQgMTE0LjkxNCA5Ny4wNzg1IDExNS40MDhDOTYuOTI2MyAxMTUuOTY1IDk2LjkyNjMgMTE2LjU2OCA5Ni45MjYzIDExNy43NzRWMTc3LjcxOUM5Ni45MjYzIDE4Mi41NDcgOTYuOTI2MyAxODQuOTYxIDk3Ljg2NTggMTg2LjgwNUM5OC42OTIyIDE4OC40MjYgMTAwLjAxMSAxODkuNzQ1IDEwMS42MzMgMTkwLjU3MUMxMDMuNDc3IDE5MS41MTEgMTA1Ljg5IDE5MS41MTEgMTEwLjcxOCAxOTEuNTExSDEyOC41MTVDMTMzLjM0MiAxOTEuNTExIDEzNS43NTYgMTkxLjUxMSAxMzcuNiAxOTAuNTcxQzEzOS4yMjEgMTg5Ljc0NSAxNDAuNTQgMTg4LjQyNiAxNDEuMzY3IDE4Ni44MDVDMTQyLjMwNiAxODQuOTYxIDE0Mi4zMDYgMTgyLjU0NyAxNDIuMzA2IDE3Ny43MTlWMTUwLjM1OUMxNDIuMzA2IDE0OS4xNTMgMTQyLjMwNiAxNDguNTUgMTQyLjE1NCAxNDcuOTkzQzE0Mi4wMTkgMTQ3LjQ5OSAxNDEuNzk3IDE0Ny4wMzMgMTQxLjQ5OSAxNDYuNjE3QzE0MS4xNjIgMTQ2LjE0NyAxNDAuNjk0IDE0NS43NjcgMTM5Ljc1OCAxNDUuMDA3TDEzMi44MzcgMTM5LjM4NUMxMzEuNzA5IDEzOC40NjggMTMxLjE0NSAxMzguMDEgMTMwLjk0IDEzNy40NTlDMTMwLjc2MSAxMzYuOTc1IDEzMC43NjEgMTM2LjQ0MyAxMzAuOTQgMTM1Ljk1OUMxMzEuMTQ1IDEzNS40MDcgMTMxLjcwOSAxMzQuOTQ5IDEzMi44MzcgMTM0LjAzM0wxMzkuNzU4IDEyOC40MUMxNDAuNjk0IDEyNy42NSAxNDEuMTYyIDEyNy4yNyAxNDEuNDk5IDEyNi44QzE0MS43OTcgMTI2LjM4NCAxNDIuMDE5IDEyNS45MTggMTQyLjE1NCAxMjUuNDI0QzE0Mi4zMDYgMTI0Ljg2NyAxNDIuMzA2IDEyNC4yNjQgMTQyLjMwNiAxMjMuMDU4VjYzLjExMjZDMTQyLjMwNiA1OC4yODUxIDE0Mi4zMDYgNTUuODcxNCAxNDEuMzY3IDU0LjAyNzZDMTQwLjU0IDUyLjQwNTcgMTM5LjIyMSA1MS4wODcgMTM3LjYgNTAuMjYwNkMxMzUuNzU2IDQ5LjMyMTEgMTMzLjM0MiA0OS4zMjExIDEyOC41MTUgNDkuMzIxMUgxMTAuNzE4QzEwNS44OSA0OS4zMjExIDEwMy40NzcgNDkuMzIxMSAxMDEuNjMzIDUwLjI2MDZDMTAwLjAxMSA1MS4wODcgOTguNjkyMiA1Mi40MDU3IDk3Ljg2NTggNTQuMDI3NloiIGZpbGw9IiMxQTI4NUYiLz4KPC9zdmc+Cg==\"\n  },\n  \"b84e4048-15dc-4dd0-8640-f4f60813c8af\": {\n    \"name\": \"NordPass\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgODAgODAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjYxMzQgNzBDMi44MjQzNSA2My4zNTIgMCA1NS4xNzIyIDAgNDYuMzI3M0MwIDI0LjA1NTIgMTcuOTA4NiA2IDQwIDZDNjIuMDkxNCA2IDgwIDI0LjA1NTIgODAgNDYuMzI3M0M4MCA1NS4xNzIxIDc3LjE3NTcgNjMuMzUxOCA3Mi4zODY3IDY5Ljk5OTlMNTMuMTc0NyAzOC41NDY2TDUxLjMxOTUgNDEuNzA0Nkw1My4yMDE4IDUwLjQ4NzdMNDAgMjcuNzE0N0wzMS44MzM0IDQxLjYxNjFMMzMuNzM0NiA1MC40ODc3TDI2LjgxNDcgMzguNTY0Nkw3LjYxMzQgNzBaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgODAgODAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjYxMzQgNzBDMi44MjQzNSA2My4zNTIgMCA1NS4xNzIyIDAgNDYuMzI3M0MwIDI0LjA1NTIgMTcuOTA4NiA2IDQwIDZDNjIuMDkxNCA2IDgwIDI0LjA1NTIgODAgNDYuMzI3M0M4MCA1NS4xNzIxIDc3LjE3NTcgNjMuMzUxOCA3Mi4zODY3IDY5Ljk5OTlMNTMuMTc0NyAzOC41NDY2TDUxLjMxOTUgNDEuNzA0Nkw1My4yMDE4IDUwLjQ4NzdMNDAgMjcuNzE0N0wzMS44MzM0IDQxLjYxNjFMMzMuNzM0NiA1MC40ODc3TDI2LjgxNDcgMzguNTY0Nkw3LjYxMzQgNzBaIiBmaWxsPSIjMENBQUFCIi8+Cjwvc3ZnPgo=\"\n  },\n  \"0ea242b4-43c4-4a1b-8b17-dd6d0b6baec6\": {\n    \"name\": \"Keeper\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzYwMzRfMzM2MjcpIj4KPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTIiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yMiAxMkMyMiAxNy41MjI4IDE3LjUyMjggMjIgMTIgMjJDNi40NzcxNSAyMiAyIDE3LjUyMjggMiAxMkMyIDYuNDc3MTUgNi40NzcxNSAyIDEyIDJDMTcuNTIyOCAyIDIyIDYuNDc3MTUgMjIgMTJaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMTAuMTIxOCAzLjI3MzI1SDExLjY2NjZWOS41MTUyN0gxNC44NTc1TDE4LjY5NiA2LjQ2MzE3TDE5LjY2MDcgNy42NjgyMUwxNS4zOTg5IDExLjA1NjRIMTAuMTIxOFYzLjI3MzI1WiIgZmlsbD0iI0ZGQzcwMCIvPgo8cGF0aCBkPSJNMTMuMTQzOCAzLjQ4MzY2TDE0LjY4ODcgMy44NzY5NFY2LjAzNDkyTDE2LjQxNzMgNC42MTgxMUwxNy43MDA4IDUuNTYwOTdMMTQuNDA3IDguMjYwMTNMMTMuMTQzOCA4LjI1MzQxVjMuNDgzNjZaIiBmaWxsPSIjRkZDNzAwIi8+CjxwYXRoIGQ9Ik00LjAzODcgMTUuMDg0OUw1LjU4MzU0IDE2LjM5NThWNy44MTQyN0w0LjAzODcgOS4yMjc3MlYxNS4wODQ5WiIgZmlsbD0iI0ZGQzcwMCIvPgo8cGF0aCBkPSJNOC42MTI1NyAxOC4yNDExTDcuMDY2MDQgMTkuNTgwNlY0LjQ5NDg1TDguNjEyNTcgNS44MzQzNFYxOC4yNDExWiIgZmlsbD0iI0ZGQzcwMCIvPgo8cGF0aCBkPSJNMTQuNjg4NyAxOC4xMTc0TDE2LjQxNzMgMTkuNTM0MkwxNy43MDA4IDE4LjU4OTdMMTQuNDA3IDE1Ljg5MjJMMTMuMTQzOCAxNS44OTg5VjIwLjY2ODdMMTQuNjg4NyAyMC4yNzU0VjE4LjExNzRaIiBmaWxsPSIjRkZDNzAwIi8+CjxwYXRoIGQ9Ik0xOC42OTYgMTcuNDc4NkwxNC44NTc1IDE0LjQyNDhIMTEuNjY2NlYyMC42NjY4SDEwLjEyMThWMTIuODg1M0gxNS4zOTg5TDE5LjY2MDcgMTYuMjczNUwxOC42OTYgMTcuNDc4NloiIGZpbGw9IiNGRkM3MDAiLz4KPHBhdGggZD0iTTE2LjczNzYgMTEuOTcwNkwxOS44OTgxIDE0LjU3MDZMMjAuODgzIDEzLjM4MjNMMTkuMTY2MSAxMS45NzA2TDIwLjg4MyAxMC41NTg4TDE5Ljg5ODEgOS4zNzA1NkwxNi43Mzc2IDExLjk3MDZaIiBmaWxsPSIjRkZDNzAwIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNjAzNF8zMzYyNyI+CjxyZWN0IHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzYwMzRfMzM2MjcpIj4KPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTIiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yMiAxMkMyMiAxNy41MjI4IDE3LjUyMjggMjIgMTIgMjJDNi40NzcxNSAyMiAyIDE3LjUyMjggMiAxMkMyIDYuNDc3MTUgNi40NzcxNSAyIDEyIDJDMTcuNTIyOCAyIDIyIDYuNDc3MTUgMjIgMTJaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMTAuMTIxOCAzLjI3MzI1SDExLjY2NjZWOS41MTUyN0gxNC44NTc1TDE4LjY5NiA2LjQ2MzE3TDE5LjY2MDcgNy42NjgyMUwxNS4zOTg5IDExLjA1NjRIMTAuMTIxOFYzLjI3MzI1WiIgZmlsbD0iI0ZGQzcwMCIvPgo8cGF0aCBkPSJNMTMuMTQzOCAzLjQ4MzY2TDE0LjY4ODcgMy44NzY5NFY2LjAzNDkyTDE2LjQxNzMgNC42MTgxMUwxNy43MDA4IDUuNTYwOTdMMTQuNDA3IDguMjYwMTNMMTMuMTQzOCA4LjI1MzQxVjMuNDgzNjZaIiBmaWxsPSIjRkZDNzAwIi8+CjxwYXRoIGQ9Ik00LjAzODcgMTUuMDg0OUw1LjU4MzU0IDE2LjM5NThWNy44MTQyN0w0LjAzODcgOS4yMjc3MlYxNS4wODQ5WiIgZmlsbD0iI0ZGQzcwMCIvPgo8cGF0aCBkPSJNOC42MTI1NyAxOC4yNDExTDcuMDY2MDQgMTkuNTgwNlY0LjQ5NDg1TDguNjEyNTcgNS44MzQzNFYxOC4yNDExWiIgZmlsbD0iI0ZGQzcwMCIvPgo8cGF0aCBkPSJNMTQuNjg4NyAxOC4xMTc0TDE2LjQxNzMgMTkuNTM0MkwxNy43MDA4IDE4LjU4OTdMMTQuNDA3IDE1Ljg5MjJMMTMuMTQzOCAxNS44OTg5VjIwLjY2ODdMMTQuNjg4NyAyMC4yNzU0VjE4LjExNzRaIiBmaWxsPSIjRkZDNzAwIi8+CjxwYXRoIGQ9Ik0xOC42OTYgMTcuNDc4NkwxNC44NTc1IDE0LjQyNDhIMTEuNjY2NlYyMC42NjY4SDEwLjEyMThWMTIuODg1M0gxNS4zOTg5TDE5LjY2MDcgMTYuMjczNUwxOC42OTYgMTcuNDc4NloiIGZpbGw9IiNGRkM3MDAiLz4KPHBhdGggZD0iTTE2LjczNzYgMTEuOTcwNkwxOS44OTgxIDE0LjU3MDZMMjAuODgzIDEzLjM4MjNMMTkuMTY2MSAxMS45NzA2TDIwLjg4MyAxMC41NTg4TDE5Ljg5ODEgOS4zNzA1NkwxNi43Mzc2IDExLjk3MDZaIiBmaWxsPSIjRkZDNzAwIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNjAzNF8zMzYyNyI+CjxyZWN0IHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K\"\n  },\n  \"891494da-2c90-4d31-a9cd-4eab0aed1309\": {\n    \"name\": \"S\\u00e9same\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cmVjdCB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiByeD0iMTc2IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMjAyXzMpIi8+CjxwYXRoIGQ9Ik03NjkuMTc1IDQ1Mi4xMThMMzcwLjU3OSA0NTEuNjc4QzMzOS42NTggNDUxLjY4NSAzMTAuMDA2IDQ2My45NjMgMjg4LjE0NCA0ODUuODEyQzI2Ni4yODIgNTA3LjY2MiAyNTQgNTM3LjI5NCAyNTQgNTY4LjE5Vjc5OC43MUMyNTQgODA3LjUzNyAyNTcuNTA5IDgxNi4wMDQgMjYzLjc1NSA4MjIuMjQ3QzI3MC4wMDIgODI4LjQ5IDI3OC40NzQgODMxLjk5OCAyODcuMzA4IDgzMkg2NTIuOTRDNjgzLjg2NCA4MzEuOTk4IDcxMy41MiA4MTkuNzIyIDczNS4zODYgNzk3Ljg3MkM3NTcuMjUyIDc3Ni4wMjIgNzY5LjUzNiA3NDYuMzg4IDc2OS41MzYgNzE1LjQ4OEw3NzAgNDU5LjYzMVY0MjMuODUzQzc2OS43ODggMzc2LjUyMyA3NTUuMTQ5IDMzMC4zODIgNzI4LjAzNCAyOTEuNTc1QzcwMC45MTkgMjUyLjc2NyA2NjIuNjE0IDIyMy4xMzYgNjE4LjIyMiAyMDYuNjI3QzU3My44MyAxOTAuMTE4IDUyNS40NTggMTg3LjUxNiA0NzkuNTQ4IDE5OS4xNjdDNDMzLjYzOSAyMTAuODE3IDM5Mi4zNzIgMjM2LjE2OCAzNjEuMjQzIDI3MS44NDIiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMzIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNNTEyLjAwMSA2OTJDNTM5LjYxNSA2OTIgNTYyLjAwMSA2NjkuNjE0IDU2Mi4wMDEgNjQyQzU2Mi4wMDEgNjE0LjM4NiA1MzkuNjE1IDU5MiA1MTIuMDAxIDU5MkM0ODQuMzg3IDU5MiA0NjIuMDAxIDYxNC4zODYgNDYyLjAwMSA2NDJDNDYyLjAwMSA2NjkuNjE0IDQ4NC4zODcgNjkyIDUxMi4wMDEgNjkyWiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIzMiIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzIwMl8zIiB4MT0iMCIgeTE9IjAiIHgyPSIxMDI0IiB5Mj0iMTAyNCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjMDA2REU2Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwQTZGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cmVjdCB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiByeD0iMTc2IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMjAyXzMpIi8+CjxwYXRoIGQ9Ik03NjkuMTc1IDQ1Mi4xMThMMzcwLjU3OSA0NTEuNjc4QzMzOS42NTggNDUxLjY4NSAzMTAuMDA2IDQ2My45NjMgMjg4LjE0NCA0ODUuODEyQzI2Ni4yODIgNTA3LjY2MiAyNTQgNTM3LjI5NCAyNTQgNTY4LjE5Vjc5OC43MUMyNTQgODA3LjUzNyAyNTcuNTA5IDgxNi4wMDQgMjYzLjc1NSA4MjIuMjQ3QzI3MC4wMDIgODI4LjQ5IDI3OC40NzQgODMxLjk5OCAyODcuMzA4IDgzMkg2NTIuOTRDNjgzLjg2NCA4MzEuOTk4IDcxMy41MiA4MTkuNzIyIDczNS4zODYgNzk3Ljg3MkM3NTcuMjUyIDc3Ni4wMjIgNzY5LjUzNiA3NDYuMzg4IDc2OS41MzYgNzE1LjQ4OEw3NzAgNDU5LjYzMVY0MjMuODUzQzc2OS43ODggMzc2LjUyMyA3NTUuMTQ5IDMzMC4zODIgNzI4LjAzNCAyOTEuNTc1QzcwMC45MTkgMjUyLjc2NyA2NjIuNjE0IDIyMy4xMzYgNjE4LjIyMiAyMDYuNjI3QzU3My44MyAxOTAuMTE4IDUyNS40NTggMTg3LjUxNiA0NzkuNTQ4IDE5OS4xNjdDNDMzLjYzOSAyMTAuODE3IDM5Mi4zNzIgMjM2LjE2OCAzNjEuMjQzIDI3MS44NDIiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMzIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNNTEyLjAwMSA2OTJDNTM5LjYxNSA2OTIgNTYyLjAwMSA2NjkuNjE0IDU2Mi4wMDEgNjQyQzU2Mi4wMDEgNjE0LjM4NiA1MzkuNjE1IDU5MiA1MTIuMDAxIDU5MkM0ODQuMzg3IDU5MiA0NjIuMDAxIDYxNC4zODYgNDYyLjAwMSA2NDJDNDYyLjAwMSA2NjkuNjE0IDQ4NC4zODcgNjkyIDUxMi4wMDEgNjkyWiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIzMiIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzIwMl8zIiB4MT0iMCIgeTE9IjAiIHgyPSIxMDI0IiB5Mj0iMTAyNCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjMDA2REU2Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwQTZGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo=\"\n  },\n  \"f3809540-7f14-49c1-a8b3-8f813b225541\": {\n    \"name\": \"Enpass\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yNTYuNDgzIDI4LjA1NTRDMzEzLjg5OSAyOC4wNTU0IDM3MS4zMTUgMjcuODg1NiA0MjguNzQ1IDI4LjE0MDVDNDQwLjY4IDI3LjkwNzYgNDUyLjUyMSAzMC4yODg5IDQ2My40NDEgMzUuMTE3OUM0NzQuMzYyIDM5Ljk0NjkgNDg0LjA5OSA0Ny4xMDczIDQ5MS45NzEgNTYuMDk4NEM1MDQuMDYyIDY5LjY5NjIgNTExLjEzMiA4Ny4wMzg3IDUxMiAxMDUuMjNDNTEyLjAyOCAxMjEuOTMzIDUxMC4wMzUgMTM4LjU3OCA1MDYuMDYzIDE1NC44MDFDNDk4LjQ0NCAxOTguNzA2IDQ5MC41MTUgMjQyLjUyNyA0ODIuNzI2IDI4Ni4zNzZDNDc2LjAxMiAzMjQuMTM0IDQ2OS41ODEgMzYxLjk1IDQ2Mi41MTMgMzk5LjY4QzQ1Ny42NzIgNDIwLjk2OSA0NDYuNTQ1IDQ0MC4zMDMgNDMwLjU4IDQ1NS4xNjVDNDE0LjYxNiA0NzAuMDI3IDM5NC41NTUgNDc5LjcyNyAzNzMuMDExIDQ4My4wMDJDMzY3Ljc1MiA0ODMuNjI5IDM2Mi40NjIgNDgzLjk0NiAzNTcuMTY2IDQ4My45NUMyOTAuMDUzIDQ4NC4wMTcgMjIyLjk0IDQ4NC4wMTcgMTU1LjgyOCA0ODMuOTVDMTMwLjQ2NiA0ODMuOSAxMDUuOTMgNDc0LjkxNSA4Ni41MTMyIDQ1OC41NjZDNjcuMDk2NSA0NDIuMjE4IDU0LjAzNjIgNDE5LjU0OCA0OS42MTggMzk0LjUyNUMzNi4xODA0IDMxOS4xNzcgMjIuNjI5NyAyNDMuODUzIDguOTY1OTcgMTY4LjU1M0M2LjI4MDM0IDE1My42MzkgMy4zMTIgMTM4LjgxMSAxLjIwNTkgMTIzLjc4NEMtMi40NjEwNSAxMDIuNzI5IDIuMzEwOTMgODEuMDc0NCAxNC40ODUyIDYzLjUyNDRDMjYuNjU5NiA0NS45NzQ1IDQ1LjI1MjkgMzMuOTQ2MiA2Ni4yMjY2IDMwLjA1MjVDNzMuMDU1NyAyOC43NDUxIDc5Ljk5NTkgMjguMTA5NCA4Ni45NDg0IDI4LjE1NDZDMTQzLjQ2IDI3Ljk5NDEgMTk5Ljk3MSAyNy45NjEgMjU2LjQ4MyAyOC4wNTU0Wk0yMTAuOTI2IDMzOS42NDNDMjEwLjkyNiAzNTQuNjcgMjEwLjkyNiAzNjkuNjk3IDIxMC45MjYgMzg0LjczOEMyMTAuNzczIDM4OC4yMDUgMjExLjM0MyAzOTEuNjY1IDIxMi41OTcgMzk0Ljg5OUMyMTMuODUyIDM5OC4xMzQgMjE1Ljc2NCA0MDEuMDcxIDIxOC4yMTMgNDAzLjUyNUMyMjAuNjYyIDQwNS45NzkgMjIzLjU5MyA0MDcuODk1IDIyNi44MjEgNDA5LjE1MkMyMzAuMDQ5IDQxMC40MDkgMjMzLjUwMyA0MTAuOTc5IDIzNi45NjIgNDEwLjgyNkMyNDkuMzg3IDQxMC44MjYgMjYxLjgxMiA0MTAuODI2IDI3NC4yMzYgNDEwLjgyNkMyNzcuOTIyIDQxMS4xODMgMjgxLjY0MiA0MTAuNzE3IDI4NS4xMjcgNDA5LjQ2MkMyODguNjEyIDQwOC4yMDggMjkxLjc3NyA0MDYuMTk2IDI5NC4zOTQgNDAzLjU3QzI5Ny4wMTIgNDAwLjk0NSAyOTkuMDE3IDM5Ny43NzIgMzAwLjI2NSAzOTQuMjc4QzMwMS41MTQgMzkwLjc4NSAzMDEuOTc1IDM4Ny4wNTggMzAxLjYxNSAzODMuMzY0QzMwMS42MTUgMzUzLjkxOSAzMDEuNjE2IDMyNC40NiAzMDEuNDc0IDI5NS4wMTVDMzAxLjMxMSAyOTMuMzMxIDMwMS42NyAyOTEuNjM3IDMwMi41MDIgMjkwLjE2NUMzMDMuMzM0IDI4OC42OTIgMzA0LjU5OSAyODcuNTEyIDMwNi4xMjUgMjg2Ljc4NkMzMjMuNzkgMjc2LjI5OCAzMzcuNTUxIDI2MC4zMTQgMzQ1LjMxMyAyNDEuMjY2QzM1My4wNzUgMjIyLjIxOSAzNTQuNDEzIDIwMS4xNTEgMzQ5LjEyMyAxODEuMjcyQzM0Mi4zNTYgMTU2Ljg1MyAzMjYuMjg2IDEzNi4wNzUgMzA0LjM3NiAxMjMuNDE0QzI4Mi40NjYgMTEwLjc1NCAyNTYuNDY5IDEwNy4yMjUgMjMxLjk4NyAxMTMuNTg2QzIxNy42NjkgMTE2LjU0NCAyMDQuMjg5IDEyMi45NTkgMTkzLjAwNyAxMzIuMjc0QzE4MS43MjYgMTQxLjU4OCAxNzIuODg0IDE1My41MjIgMTY3LjI0OSAxNjcuMDM4QzE1OS4wMjcgMTg4LjY4NiAxNTguNTQ4IDIxMi41MjEgMTY1Ljg5MyAyMzQuNDg0QzE3My4yMzggMjU2LjQ0NyAxODcuOTU0IDI3NS4xODEgMjA3LjUzMyAyODcuNDk1QzIwOC42NyAyODguMDM4IDIwOS42MTMgMjg4LjkxNyAyMTAuMjM3IDI5MC4wMTNDMjEwLjg2MSAyOTEuMTA5IDIxMS4xMzYgMjkyLjM3IDIxMS4wMjUgMjkzLjYyN0MyMTAuODQxIDMwOS4wMDggMjEwLjkyNiAzMjQuMzMzIDIxMC45MjYgMzM5LjY3MVYzMzkuNjQzWiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yNTYuNDgzIDI4LjA1NTRDMzEzLjg5OSAyOC4wNTU0IDM3MS4zMTUgMjcuODg1NiA0MjguNzQ1IDI4LjE0MDVDNDQwLjY4IDI3LjkwNzYgNDUyLjUyMSAzMC4yODg5IDQ2My40NDEgMzUuMTE3OUM0NzQuMzYyIDM5Ljk0NjkgNDg0LjA5OSA0Ny4xMDczIDQ5MS45NzEgNTYuMDk4NEM1MDQuMDYyIDY5LjY5NjIgNTExLjEzMiA4Ny4wMzg3IDUxMiAxMDUuMjNDNTEyLjAyOCAxMjEuOTMzIDUxMC4wMzUgMTM4LjU3OCA1MDYuMDYzIDE1NC44MDFDNDk4LjQ0NCAxOTguNzA2IDQ5MC41MTUgMjQyLjUyNyA0ODIuNzI2IDI4Ni4zNzZDNDc2LjAxMiAzMjQuMTM0IDQ2OS41ODEgMzYxLjk1IDQ2Mi41MTMgMzk5LjY4QzQ1Ny42NzIgNDIwLjk2OSA0NDYuNTQ1IDQ0MC4zMDMgNDMwLjU4IDQ1NS4xNjVDNDE0LjYxNiA0NzAuMDI3IDM5NC41NTUgNDc5LjcyNyAzNzMuMDExIDQ4My4wMDJDMzY3Ljc1MiA0ODMuNjI5IDM2Mi40NjIgNDgzLjk0NiAzNTcuMTY2IDQ4My45NUMyOTAuMDUzIDQ4NC4wMTcgMjIyLjk0IDQ4NC4wMTcgMTU1LjgyOCA0ODMuOTVDMTMwLjQ2NiA0ODMuOSAxMDUuOTMgNDc0LjkxNSA4Ni41MTMyIDQ1OC41NjZDNjcuMDk2NSA0NDIuMjE4IDU0LjAzNjIgNDE5LjU0OCA0OS42MTggMzk0LjUyNUMzNi4xODA0IDMxOS4xNzcgMjIuNjI5NyAyNDMuODUzIDguOTY1OTcgMTY4LjU1M0M2LjI4MDM0IDE1My42MzkgMy4zMTIgMTM4LjgxMSAxLjIwNTkgMTIzLjc4NEMtMi40NjEwNSAxMDIuNzI5IDIuMzEwOTMgODEuMDc0NCAxNC40ODUyIDYzLjUyNDRDMjYuNjU5NiA0NS45NzQ1IDQ1LjI1MjkgMzMuOTQ2MiA2Ni4yMjY2IDMwLjA1MjVDNzMuMDU1NyAyOC43NDUxIDc5Ljk5NTkgMjguMTA5NCA4Ni45NDg0IDI4LjE1NDZDMTQzLjQ2IDI3Ljk5NDEgMTk5Ljk3MSAyNy45NjEgMjU2LjQ4MyAyOC4wNTU0Wk0yMTAuOTI2IDMzOS42NDNDMjEwLjkyNiAzNTQuNjcgMjEwLjkyNiAzNjkuNjk3IDIxMC45MjYgMzg0LjczOEMyMTAuNzczIDM4OC4yMDUgMjExLjM0MyAzOTEuNjY1IDIxMi41OTcgMzk0Ljg5OUMyMTMuODUyIDM5OC4xMzQgMjE1Ljc2NCA0MDEuMDcxIDIxOC4yMTMgNDAzLjUyNUMyMjAuNjYyIDQwNS45NzkgMjIzLjU5MyA0MDcuODk1IDIyNi44MjEgNDA5LjE1MkMyMzAuMDQ5IDQxMC40MDkgMjMzLjUwMyA0MTAuOTc5IDIzNi45NjIgNDEwLjgyNkMyNDkuMzg3IDQxMC44MjYgMjYxLjgxMiA0MTAuODI2IDI3NC4yMzYgNDEwLjgyNkMyNzcuOTIyIDQxMS4xODMgMjgxLjY0MiA0MTAuNzE3IDI4NS4xMjcgNDA5LjQ2MkMyODguNjEyIDQwOC4yMDggMjkxLjc3NyA0MDYuMTk2IDI5NC4zOTQgNDAzLjU3QzI5Ny4wMTIgNDAwLjk0NSAyOTkuMDE3IDM5Ny43NzIgMzAwLjI2NSAzOTQuMjc4QzMwMS41MTQgMzkwLjc4NSAzMDEuOTc1IDM4Ny4wNTggMzAxLjYxNSAzODMuMzY0QzMwMS42MTUgMzUzLjkxOSAzMDEuNjE2IDMyNC40NiAzMDEuNDc0IDI5NS4wMTVDMzAxLjMxMSAyOTMuMzMxIDMwMS42NyAyOTEuNjM3IDMwMi41MDIgMjkwLjE2NUMzMDMuMzM0IDI4OC42OTIgMzA0LjU5OSAyODcuNTEyIDMwNi4xMjUgMjg2Ljc4NkMzMjMuNzkgMjc2LjI5OCAzMzcuNTUxIDI2MC4zMTQgMzQ1LjMxMyAyNDEuMjY2QzM1My4wNzUgMjIyLjIxOSAzNTQuNDEzIDIwMS4xNTEgMzQ5LjEyMyAxODEuMjcyQzM0Mi4zNTYgMTU2Ljg1MyAzMjYuMjg2IDEzNi4wNzUgMzA0LjM3NiAxMjMuNDE0QzI4Mi40NjYgMTEwLjc1NCAyNTYuNDY5IDEwNy4yMjUgMjMxLjk4NyAxMTMuNTg2QzIxNy42NjkgMTE2LjU0NCAyMDQuMjg5IDEyMi45NTkgMTkzLjAwNyAxMzIuMjc0QzE4MS43MjYgMTQxLjU4OCAxNzIuODg0IDE1My41MjIgMTY3LjI0OSAxNjcuMDM4QzE1OS4wMjcgMTg4LjY4NiAxNTguNTQ4IDIxMi41MjEgMTY1Ljg5MyAyMzQuNDg0QzE3My4yMzggMjU2LjQ0NyAxODcuOTU0IDI3NS4xODEgMjA3LjUzMyAyODcuNDk1QzIwOC42NyAyODguMDM4IDIwOS42MTMgMjg4LjkxNyAyMTAuMjM3IDI5MC4wMTNDMjEwLjg2MSAyOTEuMTA5IDIxMS4xMzYgMjkyLjM3IDIxMS4wMjUgMjkzLjYyN0MyMTAuODQxIDMwOS4wMDggMjEwLjkyNiAzMjQuMzMzIDIxMC45MjYgMzM5LjY3MVYzMzkuNjQzWiIgZmlsbD0iIzBEMzM4RiIvPgo8L3N2Zz4K\"\n  },\n  \"b5397666-4885-aa6b-cebf-e52262a439a2\": {\n    \"name\": \"Chromium Browser\"\n  },\n  \"771b48fd-d3d4-4f74-9232-fc157ab0507a\": {\n    \"name\": \"Edge on Mac\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50KTt9LmNscy0ye29wYWNpdHk6MC4zNTtmaWxsOnVybCgjcmFkaWFsLWdyYWRpZW50KTt9LmNscy0yLC5jbHMtNHtpc29sYXRpb246aXNvbGF0ZTt9LmNscy0ze2ZpbGw6dXJsKCNsaW5lYXItZ3JhZGllbnQtMik7fS5jbHMtNHtvcGFjaXR5OjAuNDE7ZmlsbDp1cmwoI3JhZGlhbC1ncmFkaWVudC0yKTt9LmNscy01e2ZpbGw6dXJsKCNyYWRpYWwtZ3JhZGllbnQtMyk7fS5jbHMtNntmaWxsOnVybCgjcmFkaWFsLWdyYWRpZW50LTQpO308L3N0eWxlPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iNjMuMzMiIHkxPSI4NC4wMyIgeDI9IjI0MS42NyIgeTI9Ijg0LjAzIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIC0xLCAwLCAyNjYpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMGM1OWE0Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMTE0YThiIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9InJhZGlhbC1ncmFkaWVudCIgY3g9IjE2MS44MyIgY3k9IjY4LjkxIiByPSI5NS4zOCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAtMC45NSwgMCwgMjQ4Ljg0KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMC43MiIgc3RvcC1vcGFjaXR5PSIwIi8+PHN0b3Agb2Zmc2V0PSIwLjk1IiBzdG9wLW9wYWNpdHk9IjAuNTMiLz48c3RvcCBvZmZzZXQ9IjEiLz48L3JhZGlhbEdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50LTIiIHgxPSIxNTcuMzUiIHkxPSIxNjEuMzkiIHgyPSI0NS45NiIgeTI9IjQwLjA2IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIC0xLCAwLCAyNjYpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMWI5ZGUyIi8+PHN0b3Agb2Zmc2V0PSIwLjE2IiBzdG9wLWNvbG9yPSIjMTU5NWRmIi8+PHN0b3Agb2Zmc2V0PSIwLjY3IiBzdG9wLWNvbG9yPSIjMDY4MGQ3Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA3OGQ0Ii8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9InJhZGlhbC1ncmFkaWVudC0yIiBjeD0iLTM0MC4yOSIgY3k9IjYyLjk5IiByPSIxNDMuMjQiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC4xNSwgLTAuOTksIC0wLjgsIC0wLjEyLCAxNzYuNjQsIC0xMjUuNCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAuNzYiIHN0b3Atb3BhY2l0eT0iMCIvPjxzdG9wIG9mZnNldD0iMC45NSIgc3RvcC1vcGFjaXR5PSIwLjUiLz48c3RvcCBvZmZzZXQ9IjEiLz48L3JhZGlhbEdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0icmFkaWFsLWdyYWRpZW50LTMiIGN4PSIxMTMuMzciIGN5PSI1NzAuMjEiIHI9IjIwMi40MyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC4wNCwgMSwgMi4xMywgMC4wOCwgLTExNzkuNTQsIC0xMDYuNjkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzVjMWYxIi8+PHN0b3Agb2Zmc2V0PSIwLjExIiBzdG9wLWNvbG9yPSIjMzRjMWVkIi8+PHN0b3Agb2Zmc2V0PSIwLjIzIiBzdG9wLWNvbG9yPSIjMmZjMmRmIi8+PHN0b3Agb2Zmc2V0PSIwLjMxIiBzdG9wLWNvbG9yPSIjMmJjM2QyIi8+PHN0b3Agb2Zmc2V0PSIwLjY3IiBzdG9wLWNvbG9yPSIjMzZjNzUyIi8+PC9yYWRpYWxHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9InJhZGlhbC1ncmFkaWVudC00IiBjeD0iMzc2LjUyIiBjeT0iNTY3Ljk3IiByPSI5Ny4zNCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjI4LCAwLjk2LCAwLjc4LCAtMC4yMywgLTMwMy43NiwgLTE0OC41KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzY2ZWI2ZSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzY2ZWI2ZSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHRpdGxlPkVkZ2VfTG9nb18yNjV4MjY1PC90aXRsZT48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0yMzUuNjgsMTk1LjQ2YTkzLjczLDkzLjczLDAsMCwxLTEwLjU0LDQuNzEsMTAxLjg3LDEwMS44NywwLDAsMS0zNS45LDYuNDZjLTQ3LjMyLDAtODguNTQtMzIuNTUtODguNTQtNzQuMzJBMzEuNDgsMzEuNDgsMCwwLDEsMTE3LjEzLDEwNWMtNDIuOCwxLjgtNTMuOCw0Ni40LTUzLjgsNzIuNTMsMCw3My44OCw2OC4wOSw4MS4zNyw4Mi43Niw4MS4zNyw3LjkxLDAsMTkuODQtMi4zLDI3LTQuNTZsMS4zMS0uNDRBMTI4LjM0LDEyOC4zNCwwLDAsMCwyNDEsMjAxLjEsNCw0LDAsMCwwLDIzNS42OCwxOTUuNDZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC42MyAtNC45MikiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0yMzUuNjgsMTk1LjQ2YTkzLjczLDkzLjczLDAsMCwxLTEwLjU0LDQuNzEsMTAxLjg3LDEwMS44NywwLDAsMS0zNS45LDYuNDZjLTQ3LjMyLDAtODguNTQtMzIuNTUtODguNTQtNzQuMzJBMzEuNDgsMzEuNDgsMCwwLDEsMTE3LjEzLDEwNWMtNDIuOCwxLjgtNTMuOCw0Ni40LTUzLjgsNzIuNTMsMCw3My44OCw2OC4wOSw4MS4zNyw4Mi43Niw4MS4zNyw3LjkxLDAsMTkuODQtMi4zLDI3LTQuNTZsMS4zMS0uNDRBMTI4LjM0LDEyOC4zNCwwLDAsMCwyNDEsMjAxLjEsNCw0LDAsMCwwLDIzNS42OCwxOTUuNDZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC42MyAtNC45MikiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xMTAuMzQsMjQ2LjM0QTc5LjIsNzkuMiwwLDAsMSw4Ny42LDIyNSw4MC43Miw4MC43MiwwLDAsMSwxMTcuMTMsMTA1YzMuMTItMS40Nyw4LjQ1LTQuMTMsMTUuNTQtNGEzMi4zNSwzMi4zNSwwLDAsMSwyNS42OSwxMywzMS44OCwzMS44OCwwLDAsMSw2LjM2LDE4LjY2YzAtLjIxLDI0LjQ2LTc5LjYtODAtNzkuNi00My45LDAtODAsNDEuNjYtODAsNzguMjFhMTMwLjE1LDEzMC4xNSwwLDAsMCwxMi4xMSw1NiwxMjgsMTI4LDAsMCwwLDE1Ni4zOCw2Ny4xMSw3NS41NSw3NS41NSwwLDAsMS02Mi43OC04WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQuNjMgLTQuOTIpIi8+PHBhdGggY2xhc3M9ImNscy00IiBkPSJNMTEwLjM0LDI0Ni4zNEE3OS4yLDc5LjIsMCwwLDEsODcuNiwyMjUsODAuNzIsODAuNzIsMCwwLDEsMTE3LjEzLDEwNWMzLjEyLTEuNDcsOC40NS00LjEzLDE1LjU0LTRhMzIuMzUsMzIuMzUsMCwwLDEsMjUuNjksMTMsMzEuODgsMzEuODgsMCwwLDEsNi4zNiwxOC42NmMwLS4yMSwyNC40Ni03OS42LTgwLTc5LjYtNDMuOSwwLTgwLDQxLjY2LTgwLDc4LjIxYTEzMC4xNSwxMzAuMTUsMCwwLDAsMTIuMTEsNTYsMTI4LDEyOCwwLDAsMCwxNTYuMzgsNjcuMTEsNzUuNTUsNzUuNTUsMCwwLDEtNjIuNzgtOFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC00LjYzIC00LjkyKSIvPjxwYXRoIGNsYXNzPSJjbHMtNSIgZD0iTTE1Ni45NCwxNTMuNzhjLS44MSwxLjA1LTMuMywyLjUtMy4zLDUuNjYsMCwyLjYxLDEuNyw1LjEyLDQuNzIsNy4yMywxNC4zOCwxMCw0MS40OSw4LjY4LDQxLjU2LDguNjhBNTkuNTYsNTkuNTYsMCwwLDAsMjMwLjE5LDE2N2E2MS4zOCw2MS4zOCwwLDAsMCwzMC40My01Mi44OGMuMjYtMjIuNDEtOC0zNy4zMS0xMS4zNC00My45MUMyMjguMDksMjguNzYsMTgyLjM1LDQuOTIsMTMyLjYxLDQuOTJhMTI4LDEyOCwwLDAsMC0xMjgsMTI2LjJjLjQ4LTM2LjU0LDM2LjgtNjYuMDUsODAtNjYuMDUsMy41LDAsMjMuNDYuMzQsNDIsMTAuMDcsMTYuMzQsOC41OCwyNC45LDE4Ljk0LDMwLjg1LDI5LjIxLDYuMTgsMTAuNjcsNy4yOCwyNC4xNSw3LjI4LDI5LjUyUzE2MiwxNDcuMiwxNTYuOTQsMTUzLjc4WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQuNjMgLTQuOTIpIi8+PHBhdGggY2xhc3M9ImNscy02IiBkPSJNMTU2Ljk0LDE1My43OGMtLjgxLDEuMDUtMy4zLDIuNS0zLjMsNS42NiwwLDIuNjEsMS43LDUuMTIsNC43Miw3LjIzLDE0LjM4LDEwLDQxLjQ5LDguNjgsNDEuNTYsOC42OEE1OS41Niw1OS41NiwwLDAsMCwyMzAuMTksMTY3YTYxLjM4LDYxLjM4LDAsMCwwLDMwLjQzLTUyLjg4Yy4yNi0yMi40MS04LTM3LjMxLTExLjM0LTQzLjkxQzIyOC4wOSwyOC43NiwxODIuMzUsNC45MiwxMzIuNjEsNC45MmExMjgsMTI4LDAsMCwwLTEyOCwxMjYuMmMuNDgtMzYuNTQsMzYuOC02Ni4wNSw4MC02Ni4wNSwzLjUsMCwyMy40Ni4zNCw0MiwxMC4wNywxNi4zNCw4LjU4LDI0LjksMTguOTQsMzAuODUsMjkuMjEsNi4xOCwxMC42Nyw3LjI4LDI0LjE1LDcuMjgsMjkuNTJTMTYyLDE0Ny4yLDE1Ni45NCwxNTMuNzhaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC42MyAtNC45MikiLz48L3N2Zz4=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50KTt9LmNscy0ye29wYWNpdHk6MC4zNTtmaWxsOnVybCgjcmFkaWFsLWdyYWRpZW50KTt9LmNscy0yLC5jbHMtNHtpc29sYXRpb246aXNvbGF0ZTt9LmNscy0ze2ZpbGw6dXJsKCNsaW5lYXItZ3JhZGllbnQtMik7fS5jbHMtNHtvcGFjaXR5OjAuNDE7ZmlsbDp1cmwoI3JhZGlhbC1ncmFkaWVudC0yKTt9LmNscy01e2ZpbGw6dXJsKCNyYWRpYWwtZ3JhZGllbnQtMyk7fS5jbHMtNntmaWxsOnVybCgjcmFkaWFsLWdyYWRpZW50LTQpO308L3N0eWxlPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iNjMuMzMiIHkxPSI4NC4wMyIgeDI9IjI0MS42NyIgeTI9Ijg0LjAzIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIC0xLCAwLCAyNjYpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMGM1OWE0Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMTE0YThiIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9InJhZGlhbC1ncmFkaWVudCIgY3g9IjE2MS44MyIgY3k9IjY4LjkxIiByPSI5NS4zOCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAtMC45NSwgMCwgMjQ4Ljg0KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMC43MiIgc3RvcC1vcGFjaXR5PSIwIi8+PHN0b3Agb2Zmc2V0PSIwLjk1IiBzdG9wLW9wYWNpdHk9IjAuNTMiLz48c3RvcCBvZmZzZXQ9IjEiLz48L3JhZGlhbEdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50LTIiIHgxPSIxNTcuMzUiIHkxPSIxNjEuMzkiIHgyPSI0NS45NiIgeTI9IjQwLjA2IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIC0xLCAwLCAyNjYpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMWI5ZGUyIi8+PHN0b3Agb2Zmc2V0PSIwLjE2IiBzdG9wLWNvbG9yPSIjMTU5NWRmIi8+PHN0b3Agb2Zmc2V0PSIwLjY3IiBzdG9wLWNvbG9yPSIjMDY4MGQ3Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA3OGQ0Ii8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9InJhZGlhbC1ncmFkaWVudC0yIiBjeD0iLTM0MC4yOSIgY3k9IjYyLjk5IiByPSIxNDMuMjQiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC4xNSwgLTAuOTksIC0wLjgsIC0wLjEyLCAxNzYuNjQsIC0xMjUuNCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAuNzYiIHN0b3Atb3BhY2l0eT0iMCIvPjxzdG9wIG9mZnNldD0iMC45NSIgc3RvcC1vcGFjaXR5PSIwLjUiLz48c3RvcCBvZmZzZXQ9IjEiLz48L3JhZGlhbEdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0icmFkaWFsLWdyYWRpZW50LTMiIGN4PSIxMTMuMzciIGN5PSI1NzAuMjEiIHI9IjIwMi40MyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC4wNCwgMSwgMi4xMywgMC4wOCwgLTExNzkuNTQsIC0xMDYuNjkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzVjMWYxIi8+PHN0b3Agb2Zmc2V0PSIwLjExIiBzdG9wLWNvbG9yPSIjMzRjMWVkIi8+PHN0b3Agb2Zmc2V0PSIwLjIzIiBzdG9wLWNvbG9yPSIjMmZjMmRmIi8+PHN0b3Agb2Zmc2V0PSIwLjMxIiBzdG9wLWNvbG9yPSIjMmJjM2QyIi8+PHN0b3Agb2Zmc2V0PSIwLjY3IiBzdG9wLWNvbG9yPSIjMzZjNzUyIi8+PC9yYWRpYWxHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9InJhZGlhbC1ncmFkaWVudC00IiBjeD0iMzc2LjUyIiBjeT0iNTY3Ljk3IiByPSI5Ny4zNCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjI4LCAwLjk2LCAwLjc4LCAtMC4yMywgLTMwMy43NiwgLTE0OC41KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzY2ZWI2ZSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzY2ZWI2ZSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHRpdGxlPkVkZ2VfTG9nb18yNjV4MjY1PC90aXRsZT48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0yMzUuNjgsMTk1LjQ2YTkzLjczLDkzLjczLDAsMCwxLTEwLjU0LDQuNzEsMTAxLjg3LDEwMS44NywwLDAsMS0zNS45LDYuNDZjLTQ3LjMyLDAtODguNTQtMzIuNTUtODguNTQtNzQuMzJBMzEuNDgsMzEuNDgsMCwwLDEsMTE3LjEzLDEwNWMtNDIuOCwxLjgtNTMuOCw0Ni40LTUzLjgsNzIuNTMsMCw3My44OCw2OC4wOSw4MS4zNyw4Mi43Niw4MS4zNyw3LjkxLDAsMTkuODQtMi4zLDI3LTQuNTZsMS4zMS0uNDRBMTI4LjM0LDEyOC4zNCwwLDAsMCwyNDEsMjAxLjEsNCw0LDAsMCwwLDIzNS42OCwxOTUuNDZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC42MyAtNC45MikiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0yMzUuNjgsMTk1LjQ2YTkzLjczLDkzLjczLDAsMCwxLTEwLjU0LDQuNzEsMTAxLjg3LDEwMS44NywwLDAsMS0zNS45LDYuNDZjLTQ3LjMyLDAtODguNTQtMzIuNTUtODguNTQtNzQuMzJBMzEuNDgsMzEuNDgsMCwwLDEsMTE3LjEzLDEwNWMtNDIuOCwxLjgtNTMuOCw0Ni40LTUzLjgsNzIuNTMsMCw3My44OCw2OC4wOSw4MS4zNyw4Mi43Niw4MS4zNyw3LjkxLDAsMTkuODQtMi4zLDI3LTQuNTZsMS4zMS0uNDRBMTI4LjM0LDEyOC4zNCwwLDAsMCwyNDEsMjAxLjEsNCw0LDAsMCwwLDIzNS42OCwxOTUuNDZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC42MyAtNC45MikiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xMTAuMzQsMjQ2LjM0QTc5LjIsNzkuMiwwLDAsMSw4Ny42LDIyNSw4MC43Miw4MC43MiwwLDAsMSwxMTcuMTMsMTA1YzMuMTItMS40Nyw4LjQ1LTQuMTMsMTUuNTQtNGEzMi4zNSwzMi4zNSwwLDAsMSwyNS42OSwxMywzMS44OCwzMS44OCwwLDAsMSw2LjM2LDE4LjY2YzAtLjIxLDI0LjQ2LTc5LjYtODAtNzkuNi00My45LDAtODAsNDEuNjYtODAsNzguMjFhMTMwLjE1LDEzMC4xNSwwLDAsMCwxMi4xMSw1NiwxMjgsMTI4LDAsMCwwLDE1Ni4zOCw2Ny4xMSw3NS41NSw3NS41NSwwLDAsMS02Mi43OC04WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQuNjMgLTQuOTIpIi8+PHBhdGggY2xhc3M9ImNscy00IiBkPSJNMTEwLjM0LDI0Ni4zNEE3OS4yLDc5LjIsMCwwLDEsODcuNiwyMjUsODAuNzIsODAuNzIsMCwwLDEsMTE3LjEzLDEwNWMzLjEyLTEuNDcsOC40NS00LjEzLDE1LjU0LTRhMzIuMzUsMzIuMzUsMCwwLDEsMjUuNjksMTMsMzEuODgsMzEuODgsMCwwLDEsNi4zNiwxOC42NmMwLS4yMSwyNC40Ni03OS42LTgwLTc5LjYtNDMuOSwwLTgwLDQxLjY2LTgwLDc4LjIxYTEzMC4xNSwxMzAuMTUsMCwwLDAsMTIuMTEsNTYsMTI4LDEyOCwwLDAsMCwxNTYuMzgsNjcuMTEsNzUuNTUsNzUuNTUsMCwwLDEtNjIuNzgtOFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC00LjYzIC00LjkyKSIvPjxwYXRoIGNsYXNzPSJjbHMtNSIgZD0iTTE1Ni45NCwxNTMuNzhjLS44MSwxLjA1LTMuMywyLjUtMy4zLDUuNjYsMCwyLjYxLDEuNyw1LjEyLDQuNzIsNy4yMywxNC4zOCwxMCw0MS40OSw4LjY4LDQxLjU2LDguNjhBNTkuNTYsNTkuNTYsMCwwLDAsMjMwLjE5LDE2N2E2MS4zOCw2MS4zOCwwLDAsMCwzMC40My01Mi44OGMuMjYtMjIuNDEtOC0zNy4zMS0xMS4zNC00My45MUMyMjguMDksMjguNzYsMTgyLjM1LDQuOTIsMTMyLjYxLDQuOTJhMTI4LDEyOCwwLDAsMC0xMjgsMTI2LjJjLjQ4LTM2LjU0LDM2LjgtNjYuMDUsODAtNjYuMDUsMy41LDAsMjMuNDYuMzQsNDIsMTAuMDcsMTYuMzQsOC41OCwyNC45LDE4Ljk0LDMwLjg1LDI5LjIxLDYuMTgsMTAuNjcsNy4yOCwyNC4xNSw3LjI4LDI5LjUyUzE2MiwxNDcuMiwxNTYuOTQsMTUzLjc4WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQuNjMgLTQuOTIpIi8+PHBhdGggY2xhc3M9ImNscy02IiBkPSJNMTU2Ljk0LDE1My43OGMtLjgxLDEuMDUtMy4zLDIuNS0zLjMsNS42NiwwLDIuNjEsMS43LDUuMTIsNC43Miw3LjIzLDE0LjM4LDEwLDQxLjQ5LDguNjgsNDEuNTYsOC42OEE1OS41Niw1OS41NiwwLDAsMCwyMzAuMTksMTY3YTYxLjM4LDYxLjM4LDAsMCwwLDMwLjQzLTUyLjg4Yy4yNi0yMi40MS04LTM3LjMxLTExLjM0LTQzLjkxQzIyOC4wOSwyOC43NiwxODIuMzUsNC45MiwxMzIuNjEsNC45MmExMjgsMTI4LDAsMCwwLTEyOCwxMjYuMmMuNDgtMzYuNTQsMzYuOC02Ni4wNSw4MC02Ni4wNSwzLjUsMCwyMy40Ni4zNCw0MiwxMC4wNywxNi4zNCw4LjU4LDI0LjksMTguOTQsMzAuODUsMjkuMjEsNi4xOCwxMC42Nyw3LjI4LDI0LjE1LDcuMjgsMjkuNTJTMTYyLDE0Ny4yLDE1Ni45NCwxNTMuNzhaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC42MyAtNC45MikiLz48L3N2Zz4=\"\n  },\n  \"d548826e-79b4-db40-a3d8-11116f7e8349\": {\n    \"name\": \"Bitwarden\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgdmlld0JveD0iMCAwIDMwMCAzMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF82OF82MSkiPgo8cGF0aCBkPSJNMzAwIDI1My4xMjVDMzAwIDI3OS4wMjMgMjc5LjAyMyAzMDAgMjUzLjEyNSAzMDBINDYuODc1QzIwLjk3NjYgMzAwIDAgMjc5LjAyMyAwIDI1My4xMjVWNDYuODc1QzAgMjAuOTc2NiAyMC45NzY2IDAgNDYuODc1IDBIMjUzLjEyNUMyNzkuMDIzIDAgMzAwIDIwLjk3NjYgMzAwIDQ2Ljg3NVYyNTMuMTI1WiIgZmlsbD0iIzE3NUREQyIvPgo8cGF0aCBkPSJNMjQzLjEwNSAzNy42NzU4QzI0MS4yMDEgMzUuNzcxNSAyMzguOTQ1IDM0LjgzNCAyMzYuMzY3IDM0LjgzNEg2My42MzI4QzYxLjAyNTQgMzQuODM0IDU4Ljc5ODggMzUuNzcxNSA1Ni44OTQ1IDM3LjY3NThDNTQuOTkwMiAzOS41ODAxIDU0LjA1MjcgNDEuODM1OSA1NC4wNTI3IDQ0LjQxNDFWMTU5LjU4QzU0LjA1MjcgMTY4LjE2NCA1NS43MjI3IDE3Ni42ODkgNTkuMDYyNSAxODUuMTU2QzYyLjQwMjMgMTkzLjU5NCA2Ni41NjI1IDIwMS4wOTQgNzEuNTEzNyAyMDcuNjU2Qzc2LjQ2NDggMjE0LjE4OSA4Mi4zNTM1IDIyMC41NzYgODkuMjA5IDIyNi43ODdDOTYuMDY0NSAyMzIuOTk4IDEwMi4zOTMgMjM4LjEyNSAxMDguMTY0IDI0Mi4yMjdDMTEzLjk2NSAyNDYuMzI4IDEyMCAyNTAuMTk1IDEyNi4yOTkgMjUzLjg1N0MxMzIuNTk4IDI1Ny41MiAxMzcuMDggMjU5Ljk4IDEzOS43MTcgMjYxLjI3QzE0Mi4zNTQgMjYyLjU1OSAxNDQuNDkyIDI2My41ODQgMTQ2LjA3NCAyNjQuMjU4QzE0Ny4yNzUgMjY0Ljg0NCAxNDguNTY0IDI2NS4xNjYgMTQ5Ljk3MSAyNjUuMTY2QzE1MS4zNzcgMjY1LjE2NiAxNTIuNjY2IDI2NC44NzMgMTUzLjg2NyAyNjQuMjU4QzE1NS40NzkgMjYzLjU1NSAxNTcuNTg4IDI2Mi41NTkgMTYwLjI1NCAyNjEuMjdDMTYyLjg5MSAyNTkuOTggMTY3LjM3MyAyNTcuNDkgMTczLjY3MiAyNTMuODU3QzE3OS45NzEgMjUwLjE5NSAxODYuMDA2IDI0Ni4zMjggMTkxLjgwNyAyNDIuMjI3QzE5Ny42MDcgMjM4LjEyNSAyMDMuOTM2IDIzMi45NjkgMjEwLjc5MSAyMjYuNzg3QzIxNy42NDYgMjIwLjU3NiAyMjMuNTM1IDIxNC4yMTkgMjI4LjQ4NiAyMDcuNjU2QzIzMy40MzggMjAxLjA5NCAyMzcuNTY4IDE5My42MjMgMjQwLjkzOCAxODUuMTU2QzI0NC4yNzcgMTc2LjcxOSAyNDUuOTQ3IDE2OC4xOTMgMjQ1Ljk0NyAxNTkuNThWNDQuNDQzNEMyNDUuOTc3IDQxLjgzNTkgMjQ1LjAxIDM5LjU4MDEgMjQzLjEwNSAzNy42NzU4Wk0yMjAuODQgMTYwLjY2NEMyMjAuODQgMjAyLjM1NCAxNTAgMjM4LjI3MSAxNTAgMjM4LjI3MVY1OS41MDJIMjIwLjg0QzIyMC44NCA1OS41MDIgMjIwLjg0IDExOC45NzUgMjIwLjg0IDE2MC42NjRaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzY4XzYxIj4KPHJlY3Qgd2lkdGg9IjMwMCIgaGVpZ2h0PSIzMDAiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgdmlld0JveD0iMCAwIDMwMCAzMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF82OF82MSkiPgo8cGF0aCBkPSJNMzAwIDI1My4xMjVDMzAwIDI3OS4wMjMgMjc5LjAyMyAzMDAgMjUzLjEyNSAzMDBINDYuODc1QzIwLjk3NjYgMzAwIDAgMjc5LjAyMyAwIDI1My4xMjVWNDYuODc1QzAgMjAuOTc2NiAyMC45NzY2IDAgNDYuODc1IDBIMjUzLjEyNUMyNzkuMDIzIDAgMzAwIDIwLjk3NjYgMzAwIDQ2Ljg3NVYyNTMuMTI1WiIgZmlsbD0iIzE3NUREQyIvPgo8cGF0aCBkPSJNMjQzLjEwNSAzNy42NzU4QzI0MS4yMDEgMzUuNzcxNSAyMzguOTQ1IDM0LjgzNCAyMzYuMzY3IDM0LjgzNEg2My42MzI4QzYxLjAyNTQgMzQuODM0IDU4Ljc5ODggMzUuNzcxNSA1Ni44OTQ1IDM3LjY3NThDNTQuOTkwMiAzOS41ODAxIDU0LjA1MjcgNDEuODM1OSA1NC4wNTI3IDQ0LjQxNDFWMTU5LjU4QzU0LjA1MjcgMTY4LjE2NCA1NS43MjI3IDE3Ni42ODkgNTkuMDYyNSAxODUuMTU2QzYyLjQwMjMgMTkzLjU5NCA2Ni41NjI1IDIwMS4wOTQgNzEuNTEzNyAyMDcuNjU2Qzc2LjQ2NDggMjE0LjE4OSA4Mi4zNTM1IDIyMC41NzYgODkuMjA5IDIyNi43ODdDOTYuMDY0NSAyMzIuOTk4IDEwMi4zOTMgMjM4LjEyNSAxMDguMTY0IDI0Mi4yMjdDMTEzLjk2NSAyNDYuMzI4IDEyMCAyNTAuMTk1IDEyNi4yOTkgMjUzLjg1N0MxMzIuNTk4IDI1Ny41MiAxMzcuMDggMjU5Ljk4IDEzOS43MTcgMjYxLjI3QzE0Mi4zNTQgMjYyLjU1OSAxNDQuNDkyIDI2My41ODQgMTQ2LjA3NCAyNjQuMjU4QzE0Ny4yNzUgMjY0Ljg0NCAxNDguNTY0IDI2NS4xNjYgMTQ5Ljk3MSAyNjUuMTY2QzE1MS4zNzcgMjY1LjE2NiAxNTIuNjY2IDI2NC44NzMgMTUzLjg2NyAyNjQuMjU4QzE1NS40NzkgMjYzLjU1NSAxNTcuNTg4IDI2Mi41NTkgMTYwLjI1NCAyNjEuMjdDMTYyLjg5MSAyNTkuOTggMTY3LjM3MyAyNTcuNDkgMTczLjY3MiAyNTMuODU3QzE3OS45NzEgMjUwLjE5NSAxODYuMDA2IDI0Ni4zMjggMTkxLjgwNyAyNDIuMjI3QzE5Ny42MDcgMjM4LjEyNSAyMDMuOTM2IDIzMi45NjkgMjEwLjc5MSAyMjYuNzg3QzIxNy42NDYgMjIwLjU3NiAyMjMuNTM1IDIxNC4yMTkgMjI4LjQ4NiAyMDcuNjU2QzIzMy40MzggMjAxLjA5NCAyMzcuNTY4IDE5My42MjMgMjQwLjkzOCAxODUuMTU2QzI0NC4yNzcgMTc2LjcxOSAyNDUuOTQ3IDE2OC4xOTMgMjQ1Ljk0NyAxNTkuNThWNDQuNDQzNEMyNDUuOTc3IDQxLjgzNTkgMjQ1LjAxIDM5LjU4MDEgMjQzLjEwNSAzNy42NzU4Wk0yMjAuODQgMTYwLjY2NEMyMjAuODQgMjAyLjM1NCAxNTAgMjM4LjI3MSAxNTAgMjM4LjI3MVY1OS41MDJIMjIwLjg0QzIyMC44NCA1OS41MDIgMjIwLjg0IDExOC45NzUgMjIwLjg0IDE2MC42NjRaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzY4XzYxIj4KPHJlY3Qgd2lkdGg9IjMwMCIgaGVpZ2h0PSIzMDAiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==\"\n  },\n  \"fbfc3007-154e-4ecc-8c0b-6e020557d7bd\": {\n    \"name\": \"iCloud Keychain\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJub25lIj48cGF0aCBkPSJtMjE3LjM2LDkwLjY5Yy0xNS41OCw5LjU0LTI1LjE3LDI2LjQxLTI1LjM4LDQ0LjY4LjA2LDIwLjY3LDEyLjQzLDM5LjMyLDMxLjQ2LDQ3LjQxLTMuNjcsMTEuODQtOS4xLDIzLjA2LTE2LjExLDMzLjI4LTEwLjAzLDE0LjQ0LTIwLjUyLDI4Ljg3LTM2LjQ3LDI4Ljg3cy0yMC4wNi05LjI3LTM4LjQ1LTkuMjctMjQuMzIsOS41Ny0zOC45LDkuNTctMjQuNzctMTMuMzctMzYuNDctMjkuNzljLTE1LjQ2LTIyLjk5LTIzLjk1LTQ5Ljk2LTI0LjQ3LTc3LjY2LDAtNDUuNTksMjkuNjMtNjkuNzUsNTguODEtNjkuNzUsMTUuNSwwLDI4LjQyLDEwLjE4LDM4LjE1LDEwLjE4czIzLjcxLTEwLjc5LDQxLjM0LTEwLjc5YzE4LjQxLS40NywzNS44NCw4LjI0LDQ2LjUsMjMuMjVabS01NC44Ni00Mi41NWM3Ljc3LTkuMTQsMTIuMTctMjAuNjcsMTIuNDYtMzIuNjcuMDEtMS41OC0uMTQtMy4xNi0uNDYtNC43MS0xMy4zNSwxLjMtMjUuNjksNy42Ny0zNC41LDE3Ljc4LTcuODUsOC43OC0xMi40MSwyMC0xMi45MiwzMS43NiwwLDEuNDMuMTYsMi44Ni40Niw0LjI2LDEuMDUuMiwyLjEyLjMsMy4xOS4zLDEyLjQzLS45OSwyMy45MS03LjA0LDMxLjc2LTE2LjczWiIgZmlsbD0iI0ZGRiIvPjwvc3ZnPg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJub25lIj48cGF0aCBkPSJtMjE3LjM2LDkwLjY5Yy0xNS41OCw5LjU0LTI1LjE3LDI2LjQxLTI1LjM4LDQ0LjY4LjA2LDIwLjY3LDEyLjQzLDM5LjMyLDMxLjQ2LDQ3LjQxLTMuNjcsMTEuODQtOS4xLDIzLjA2LTE2LjExLDMzLjI4LTEwLjAzLDE0LjQ0LTIwLjUyLDI4Ljg3LTM2LjQ3LDI4Ljg3cy0yMC4wNi05LjI3LTM4LjQ1LTkuMjctMjQuMzIsOS41Ny0zOC45LDkuNTctMjQuNzctMTMuMzctMzYuNDctMjkuNzljLTE1LjQ2LTIyLjk5LTIzLjk1LTQ5Ljk2LTI0LjQ3LTc3LjY2LDAtNDUuNTksMjkuNjMtNjkuNzUsNTguODEtNjkuNzUsMTUuNSwwLDI4LjQyLDEwLjE4LDM4LjE1LDEwLjE4czIzLjcxLTEwLjc5LDQxLjM0LTEwLjc5YzE4LjQxLS40NywzNS44NCw4LjI0LDQ2LjUsMjMuMjVabS01NC44Ni00Mi41NWM3Ljc3LTkuMTQsMTIuMTctMjAuNjcsMTIuNDYtMzIuNjcuMDEtMS41OC0uMTQtMy4xNi0uNDYtNC43MS0xMy4zNSwxLjMtMjUuNjksNy42Ny0zNC41LDE3Ljc4LTcuODUsOC43OC0xMi40MSwyMC0xMi45MiwzMS43NiwwLDEuNDMuMTYsMi44Ni40Niw0LjI2LDEuMDUuMiwyLjEyLjMsMy4xOS4zLDEyLjQzLS45OSwyMy45MS03LjA0LDMxLjc2LTE2LjczWiIgZmlsbD0iIzAwMCIvPjwvc3ZnPg==\"\n  },\n  \"66a0ccb3-bd6a-191f-ee06-e375c50b9846\": {\n    \"name\": \"Thales Bio iOS SDK\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiMyQzJGNzMiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiM1RUJGRDQiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\"\n  },\n  \"8836336a-f590-0921-301d-46427531eee6\": {\n    \"name\": \"Thales Bio Android SDK\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiMyQzJGNzMiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiM1RUJGRDQiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\"\n  },\n  \"cd69adb5-3c7a-deb9-3177-6800ea6cb72a\": {\n    \"name\": \"Thales PIN Android SDK\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiMyQzJGNzMiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiM1RUJGRDQiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\"\n  },\n  \"17290f1e-c212-34d0-1423-365d729f09d9\": {\n    \"name\": \"Thales PIN iOS SDK\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny42IDc3LjYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDc3LjYgNzcuNjsiPjxnPjxwYXRoIGZpbGw9IiMyQzJGNzMiIGQ9Ik03Ny42LDU1LjFjLTQuOSwxLjQtMTEuNCwxLjktMTYuMiwybC0yMi43LTQ1LjhoLTEuM2wtMjIuNiw0NS44Yy00LjgtMC4xLTEwLjUtMC42LTE1LjQtMmwyOC43LTUzLjdoMjAuNSBMNzcuNiw1NS4xeiI+PC9wYXRoPjxwYXRoIGZpbGw9IiM1RUJGRDQiIGQ9Ik00Ny43LDQxLjRjMCw1LjMtNC4zLDkuNS05LjYsOS41Yy01LjMsMC05LjUtNC4zLTkuNS05LjVjMC01LjMsNC4zLTkuNSw5LjUtOS41IEM0My40LDMxLjksNDcuNywzNi4xLDQ3LjcsNDEuNCI+PC9wYXRoPjwvZz48L3N2Zz4=\"\n  },\n  \"50726f74-6f6e-5061-7373-50726f746f6e\": {\n    \"name\": \"Proton Pass\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMzUzLjI3NCAyMTQuMzkxQzQwOC44MzUgMTU4LjgzMiA0MzYuNjE0IDEzMS4wNTIgNDY4LjY0OCAxMjAuNjQ0QzQ5Ni44MjUgMTExLjQ4OSA1MjcuMTc1IDExMS40ODkgNTU1LjM1MiAxMjAuNjQ0QzU4Ny4zODYgMTMxLjA1MiA2MTUuMTY1IDE1OC44MzIgNjcwLjcyNiAyMTQuMzkxTDgwOS42MDggMzUzLjI3NEM4NjUuMTY5IDQwOC44MzUgODkyLjk0OCA0MzYuNjE0IDkwMy4zNTYgNDY4LjY0OEM5MTIuNTEyIDQ5Ni44MjUgOTEyLjUxMiA1MjcuMTc1IDkwMy4zNTYgNTU1LjM1MkM4OTIuOTQ4IDU4Ny4zODYgODY1LjE2OSA2MTUuMTY1IDgwOS42MDggNjcwLjcyNkw2NzAuNzI2IDgwOS42MDhDNjE1LjE2NSA4NjUuMTY5IDU4Ny4zODYgODkyLjk0OCA1NTUuMzUyIDkwMy4zNTZDNTI3LjE3NSA5MTIuNTEyIDQ5Ni44MjUgOTEyLjUxMiA0NjguNjQ4IDkwMy4zNTZDNDM2LjYxNCA4OTIuOTQ4IDQwOC44MzUgODY1LjE2OSAzNTMuMjc0IDgwOS42MDhMMzI3LjE2MiA3ODAuMzM2QzMxMS4zNjEgNzYyLjYyMSAzMDMuNDYyIDc1My43NjMgMjk3LjgyNyA3NDMuNjg4QzI5Mi44MzMgNzM0Ljc1NCAyODkuMTY2IDcyNS4xMzcgMjg2Ljk0NyA3MTUuMTQ0QzI4NC40NDQgNzAzLjg3OCAyODQuNDQ0IDY5Mi4wMDggMjg0LjQ0NCA2NjguMjcxVjM1NS43MjlDMjg0LjQ0NCAzMzEuOTkyIDI4NC40NDQgMzIwLjEyMiAyODYuOTQ3IDMwOC44NTVDMjg5LjE2NiAyOTguODYzIDI5Mi44MzMgMjg5LjI0NiAyOTcuODI3IDI4MC4zMTFDMzAzLjQ2MiAyNzAuMjM3IDMxMS4zNjEgMjYxLjM4IDMyNy4xNjIgMjQzLjY2NUwzNTMuMjc0IDIxNC4zOTFaIiBmaWxsPSJ1cmwoI3BhaW50MF9yYWRpYWxfMTIxNTVfNDE4NzMpIi8+CjxwYXRoIGQ9Ik0zNTMuMjc0IDIxNC4zOTFDNDA4LjgzNSAxNTguODMyIDQzNi42MTQgMTMxLjA1MiA0NjguNjQ4IDEyMC42NDRDNDk2LjgyNSAxMTEuNDg5IDUyNy4xNzUgMTExLjQ4OSA1NTUuMzUyIDEyMC42NDRDNTg3LjM4NiAxMzEuMDUyIDYxNS4xNjUgMTU4LjgzMiA2NzAuNzI2IDIxNC4zOTFMODA5LjYwOCAzNTMuMjc0Qzg2NS4xNjkgNDA4LjgzNSA4OTIuOTQ4IDQzNi42MTQgOTAzLjM1NiA0NjguNjQ4QzkxMi41MTIgNDk2LjgyNSA5MTIuNTEyIDUyNy4xNzUgOTAzLjM1NiA1NTUuMzUyQzg5Mi45NDggNTg3LjM4NiA4NjUuMTY5IDYxNS4xNjUgODA5LjYwOCA2NzAuNzI2TDY3MC43MjYgODA5LjYwOEM2MTUuMTY1IDg2NS4xNjkgNTg3LjM4NiA4OTIuOTQ4IDU1NS4zNTIgOTAzLjM1NkM1MjcuMTc1IDkxMi41MTIgNDk2LjgyNSA5MTIuNTEyIDQ2OC42NDggOTAzLjM1NkM0MzYuNjE0IDg5Mi45NDggNDA4LjgzNSA4NjUuMTY5IDM1My4yNzQgODA5LjYwOEwzMjcuMTYyIDc4MC4zMzZDMzExLjM2MSA3NjIuNjIxIDMwMy40NjIgNzUzLjc2MyAyOTcuODI3IDc0My42ODhDMjkyLjgzMyA3MzQuNzU0IDI4OS4xNjYgNzI1LjEzNyAyODYuOTQ3IDcxNS4xNDRDMjg0LjQ0NCA3MDMuODc4IDI4NC40NDQgNjkyLjAwOCAyODQuNDQ0IDY2OC4yNzFWMzU1LjcyOUMyODQuNDQ0IDMzMS45OTIgMjg0LjQ0NCAzMjAuMTIyIDI4Ni45NDcgMzA4Ljg1NUMyODkuMTY2IDI5OC44NjMgMjkyLjgzMyAyODkuMjQ2IDI5Ny44MjcgMjgwLjMxMUMzMDMuNDYyIDI3MC4yMzcgMzExLjM2MSAyNjEuMzggMzI3LjE2MiAyNDMuNjY1TDM1My4yNzQgMjE0LjM5MVoiIGZpbGw9InVybCgjcGFpbnQxX2xpbmVhcl8xMjE1NV80MTg3MykiLz4KPHBhdGggZD0iTTM0My4zNTYgMjI0LjMwM0MzNzEuMTM1IDE5Ni41MjQgMzg1LjAyNCAxODIuNjM0IDQwMS4wNDEgMTc3LjQzQzQxNS4xMyAxNzIuODUyIDQzMC4zMDUgMTcyLjg1MiA0NDQuMzkzIDE3Ny40M0M0NjAuNDEgMTgyLjYzNCA0NzQuMyAxOTYuNTI0IDUwMi4wNzkgMjI0LjMwM0w3MTAuNDA2IDQzMi42MjlDNzM4LjE4NSA0NjAuNDA4IDc1Mi4wNzQgNDc0LjMgNzU3LjI3OSA0OTAuMzE0Qzc2MS44NTYgNTA0LjQwMyA3NjEuODU2IDUxOS41ODEgNzU3LjI3OSA1MzMuNjY5Qzc1Mi4wNzQgNTQ5LjY4NiA3MzguMTg1IDU2My41NzYgNzEwLjQwNiA1OTEuMzU0TDUwMi4wNzkgNzk5LjY4MUM0NzQuMyA4MjcuNDYgNDYwLjQxIDg0MS4zNSA0NDQuMzkzIDg0Ni41NTVDNDMwLjMwNSA4NTEuMTMyIDQxNS4xMyA4NTEuMTMyIDQwMS4wNDEgODQ2LjU1NUMzODUuMDI0IDg0MS4zNSAzNzEuMTM1IDgyNy40NiAzNDMuMzU2IDc5OS42ODFMMjE0LjM5MSA2NzAuNzE3QzE1OC44MzIgNjE1LjE1NyAxMzEuMDUyIDU4Ny4zNzggMTIwLjY0NCA1NTUuMzQ2QzExMS40ODkgNTI3LjE2NyAxMTEuNDg5IDQ5Ni44MTYgMTIwLjY0NCA0NjguNjM5QzEzMS4wNTIgNDM2LjYwNSAxNTguODMyIDQwOC44MjYgMjE0LjM5MSAzNTMuMjY2TDM0My4zNTYgMjI0LjMwM1oiIGZpbGw9InVybCgjcGFpbnQyX3JhZGlhbF8xMjE1NV80MTg3MykiLz4KPGRlZnM+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQwX3JhZGlhbF8xMjE1NV80MTg3MyIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgzNTQuMTQ1IDkyNS43NDQpIHJvdGF0ZSgtNTguMTQyKSBzY2FsZSg2NjQuODM1IDEwMzkuNjcpIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRDU4MCIvPgo8c3RvcCBvZmZzZXQ9IjAuMDkzNzUiIHN0b3AtY29sb3I9IiNGNkM1OTIiLz4KPHN0b3Agb2Zmc2V0PSIwLjIwNSIgc3RvcC1jb2xvcj0iI0VCQjZBMiIvPgo8c3RvcCBvZmZzZXQ9IjAuMzI0NDY2IiBzdG9wLWNvbG9yPSIjREZBNUFGIi8+CjxzdG9wIG9mZnNldD0iMC40Mjg3NSIgc3RvcC1jb2xvcj0iI0QzOTdCRSIvPgo8c3RvcCBvZmZzZXQ9IjAuNTMzNzUiIHN0b3AtY29sb3I9IiNDNDg2Q0IiLz4KPHN0b3Agb2Zmc2V0PSIwLjY0ODc1IiBzdG9wLWNvbG9yPSIjQjU3OEQ5Ii8+CjxzdG9wIG9mZnNldD0iMC43NzEyNSIgc3RvcC1jb2xvcj0iI0ExNjZFNSIvPgo8c3RvcCBvZmZzZXQ9IjAuODkxMjUiIHN0b3AtY29sb3I9IiM4QjU3RjIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNzA0Q0ZGIi8+CjwvcmFkaWFsR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl8xMjE1NV80MTg3MyIgeDE9IjMyNi43MTMiIHkxPSItNDQuMzY0OSIgeDI9IjQ4MS41OTgiIHkyPSI5MDEuMDc3IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM2RDRBRkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjM5MjAwOSIgc3RvcC1jb2xvcj0iI0IzOUZGQiIgc3RvcC1vcGFjaXR5PSIwLjk3ODAyMiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkU4REIiIHN0b3Atb3BhY2l0eT0iMC44Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQyX3JhZGlhbF8xMjE1NV80MTg3MyIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgyOTUuNzQ2IDg0NC43ODYpIHJvdGF0ZSgtNTguMTQyKSBzY2FsZSg1MzQuNzU4IDgzNi4yNTUpIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRDU4MCIvPgo8c3RvcCBvZmZzZXQ9IjAuMDkzNzUiIHN0b3AtY29sb3I9IiNGNkM1OTIiLz4KPHN0b3Agb2Zmc2V0PSIwLjIwNSIgc3RvcC1jb2xvcj0iI0VCQjZBMiIvPgo8c3RvcCBvZmZzZXQ9IjAuMzI0NDY2IiBzdG9wLWNvbG9yPSIjREZBNUFGIi8+CjxzdG9wIG9mZnNldD0iMC40Mjg3NSIgc3RvcC1jb2xvcj0iI0QzOTdCRSIvPgo8c3RvcCBvZmZzZXQ9IjAuNTMzNzUiIHN0b3AtY29sb3I9IiNDNDg2Q0IiLz4KPHN0b3Agb2Zmc2V0PSIwLjY0ODc1IiBzdG9wLWNvbG9yPSIjQjU3OEQ5Ii8+CjxzdG9wIG9mZnNldD0iMC43NzEyNSIgc3RvcC1jb2xvcj0iI0ExNjZFNSIvPgo8c3RvcCBvZmZzZXQ9IjAuODkxMjUiIHN0b3AtY29sb3I9IiM4QjU3RjIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNzA0Q0ZGIi8+CjwvcmFkaWFsR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMzUzLjI3NCAyMTQuMzkxQzQwOC44MzUgMTU4LjgzMiA0MzYuNjE0IDEzMS4wNTIgNDY4LjY0OCAxMjAuNjQ0QzQ5Ni44MjUgMTExLjQ4OSA1MjcuMTc1IDExMS40ODkgNTU1LjM1MiAxMjAuNjQ0QzU4Ny4zODYgMTMxLjA1MiA2MTUuMTY1IDE1OC44MzIgNjcwLjcyNiAyMTQuMzkxTDgwOS42MDggMzUzLjI3NEM4NjUuMTY5IDQwOC44MzUgODkyLjk0OCA0MzYuNjE0IDkwMy4zNTYgNDY4LjY0OEM5MTIuNTEyIDQ5Ni44MjUgOTEyLjUxMiA1MjcuMTc1IDkwMy4zNTYgNTU1LjM1MkM4OTIuOTQ4IDU4Ny4zODYgODY1LjE2OSA2MTUuMTY1IDgwOS42MDggNjcwLjcyNkw2NzAuNzI2IDgwOS42MDhDNjE1LjE2NSA4NjUuMTY5IDU4Ny4zODYgODkyLjk0OCA1NTUuMzUyIDkwMy4zNTZDNTI3LjE3NSA5MTIuNTEyIDQ5Ni44MjUgOTEyLjUxMiA0NjguNjQ4IDkwMy4zNTZDNDM2LjYxNCA4OTIuOTQ4IDQwOC44MzUgODY1LjE2OSAzNTMuMjc0IDgwOS42MDhMMzI3LjE2MiA3ODAuMzM2QzMxMS4zNjEgNzYyLjYyMSAzMDMuNDYyIDc1My43NjMgMjk3LjgyNyA3NDMuNjg4QzI5Mi44MzMgNzM0Ljc1NCAyODkuMTY2IDcyNS4xMzcgMjg2Ljk0NyA3MTUuMTQ0QzI4NC40NDQgNzAzLjg3OCAyODQuNDQ0IDY5Mi4wMDggMjg0LjQ0NCA2NjguMjcxVjM1NS43MjlDMjg0LjQ0NCAzMzEuOTkyIDI4NC40NDQgMzIwLjEyMiAyODYuOTQ3IDMwOC44NTVDMjg5LjE2NiAyOTguODYzIDI5Mi44MzMgMjg5LjI0NiAyOTcuODI3IDI4MC4zMTFDMzAzLjQ2MiAyNzAuMjM3IDMxMS4zNjEgMjYxLjM4IDMyNy4xNjIgMjQzLjY2NUwzNTMuMjc0IDIxNC4zOTFaIiBmaWxsPSJ1cmwoI3BhaW50MF9yYWRpYWxfMTIxNTVfNDE4NzMpIi8+CjxwYXRoIGQ9Ik0zNTMuMjc0IDIxNC4zOTFDNDA4LjgzNSAxNTguODMyIDQzNi42MTQgMTMxLjA1MiA0NjguNjQ4IDEyMC42NDRDNDk2LjgyNSAxMTEuNDg5IDUyNy4xNzUgMTExLjQ4OSA1NTUuMzUyIDEyMC42NDRDNTg3LjM4NiAxMzEuMDUyIDYxNS4xNjUgMTU4LjgzMiA2NzAuNzI2IDIxNC4zOTFMODA5LjYwOCAzNTMuMjc0Qzg2NS4xNjkgNDA4LjgzNSA4OTIuOTQ4IDQzNi42MTQgOTAzLjM1NiA0NjguNjQ4QzkxMi41MTIgNDk2LjgyNSA5MTIuNTEyIDUyNy4xNzUgOTAzLjM1NiA1NTUuMzUyQzg5Mi45NDggNTg3LjM4NiA4NjUuMTY5IDYxNS4xNjUgODA5LjYwOCA2NzAuNzI2TDY3MC43MjYgODA5LjYwOEM2MTUuMTY1IDg2NS4xNjkgNTg3LjM4NiA4OTIuOTQ4IDU1NS4zNTIgOTAzLjM1NkM1MjcuMTc1IDkxMi41MTIgNDk2LjgyNSA5MTIuNTEyIDQ2OC42NDggOTAzLjM1NkM0MzYuNjE0IDg5Mi45NDggNDA4LjgzNSA4NjUuMTY5IDM1My4yNzQgODA5LjYwOEwzMjcuMTYyIDc4MC4zMzZDMzExLjM2MSA3NjIuNjIxIDMwMy40NjIgNzUzLjc2MyAyOTcuODI3IDc0My42ODhDMjkyLjgzMyA3MzQuNzU0IDI4OS4xNjYgNzI1LjEzNyAyODYuOTQ3IDcxNS4xNDRDMjg0LjQ0NCA3MDMuODc4IDI4NC40NDQgNjkyLjAwOCAyODQuNDQ0IDY2OC4yNzFWMzU1LjcyOUMyODQuNDQ0IDMzMS45OTIgMjg0LjQ0NCAzMjAuMTIyIDI4Ni45NDcgMzA4Ljg1NUMyODkuMTY2IDI5OC44NjMgMjkyLjgzMyAyODkuMjQ2IDI5Ny44MjcgMjgwLjMxMUMzMDMuNDYyIDI3MC4yMzcgMzExLjM2MSAyNjEuMzggMzI3LjE2MiAyNDMuNjY1TDM1My4yNzQgMjE0LjM5MVoiIGZpbGw9InVybCgjcGFpbnQxX2xpbmVhcl8xMjE1NV80MTg3MykiLz4KPHBhdGggZD0iTTM0My4zNTYgMjI0LjMwM0MzNzEuMTM1IDE5Ni41MjQgMzg1LjAyNCAxODIuNjM0IDQwMS4wNDEgMTc3LjQzQzQxNS4xMyAxNzIuODUyIDQzMC4zMDUgMTcyLjg1MiA0NDQuMzkzIDE3Ny40M0M0NjAuNDEgMTgyLjYzNCA0NzQuMyAxOTYuNTI0IDUwMi4wNzkgMjI0LjMwM0w3MTAuNDA2IDQzMi42MjlDNzM4LjE4NSA0NjAuNDA4IDc1Mi4wNzQgNDc0LjMgNzU3LjI3OSA0OTAuMzE0Qzc2MS44NTYgNTA0LjQwMyA3NjEuODU2IDUxOS41ODEgNzU3LjI3OSA1MzMuNjY5Qzc1Mi4wNzQgNTQ5LjY4NiA3MzguMTg1IDU2My41NzYgNzEwLjQwNiA1OTEuMzU0TDUwMi4wNzkgNzk5LjY4MUM0NzQuMyA4MjcuNDYgNDYwLjQxIDg0MS4zNSA0NDQuMzkzIDg0Ni41NTVDNDMwLjMwNSA4NTEuMTMyIDQxNS4xMyA4NTEuMTMyIDQwMS4wNDEgODQ2LjU1NUMzODUuMDI0IDg0MS4zNSAzNzEuMTM1IDgyNy40NiAzNDMuMzU2IDc5OS42ODFMMjE0LjM5MSA2NzAuNzE3QzE1OC44MzIgNjE1LjE1NyAxMzEuMDUyIDU4Ny4zNzggMTIwLjY0NCA1NTUuMzQ2QzExMS40ODkgNTI3LjE2NyAxMTEuNDg5IDQ5Ni44MTYgMTIwLjY0NCA0NjguNjM5QzEzMS4wNTIgNDM2LjYwNSAxNTguODMyIDQwOC44MjYgMjE0LjM5MSAzNTMuMjY2TDM0My4zNTYgMjI0LjMwM1oiIGZpbGw9InVybCgjcGFpbnQyX3JhZGlhbF8xMjE1NV80MTg3MykiLz4KPGRlZnM+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQwX3JhZGlhbF8xMjE1NV80MTg3MyIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgzNTQuMTQ1IDkyNS43NDQpIHJvdGF0ZSgtNTguMTQyKSBzY2FsZSg2NjQuODM1IDEwMzkuNjcpIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRDU4MCIvPgo8c3RvcCBvZmZzZXQ9IjAuMDkzNzUiIHN0b3AtY29sb3I9IiNGNkM1OTIiLz4KPHN0b3Agb2Zmc2V0PSIwLjIwNSIgc3RvcC1jb2xvcj0iI0VCQjZBMiIvPgo8c3RvcCBvZmZzZXQ9IjAuMzI0NDY2IiBzdG9wLWNvbG9yPSIjREZBNUFGIi8+CjxzdG9wIG9mZnNldD0iMC40Mjg3NSIgc3RvcC1jb2xvcj0iI0QzOTdCRSIvPgo8c3RvcCBvZmZzZXQ9IjAuNTMzNzUiIHN0b3AtY29sb3I9IiNDNDg2Q0IiLz4KPHN0b3Agb2Zmc2V0PSIwLjY0ODc1IiBzdG9wLWNvbG9yPSIjQjU3OEQ5Ii8+CjxzdG9wIG9mZnNldD0iMC43NzEyNSIgc3RvcC1jb2xvcj0iI0ExNjZFNSIvPgo8c3RvcCBvZmZzZXQ9IjAuODkxMjUiIHN0b3AtY29sb3I9IiM4QjU3RjIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNzA0Q0ZGIi8+CjwvcmFkaWFsR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl8xMjE1NV80MTg3MyIgeDE9IjMyNi43MTMiIHkxPSItNDQuMzY0OSIgeDI9IjQ4MS41OTgiIHkyPSI5MDEuMDc3IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM2RDRBRkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjM5MjAwOSIgc3RvcC1jb2xvcj0iI0IzOUZGQiIgc3RvcC1vcGFjaXR5PSIwLjk3ODAyMiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkU4REIiIHN0b3Atb3BhY2l0eT0iMC44Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQyX3JhZGlhbF8xMjE1NV80MTg3MyIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgyOTUuNzQ2IDg0NC43ODYpIHJvdGF0ZSgtNTguMTQyKSBzY2FsZSg1MzQuNzU4IDgzNi4yNTUpIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRDU4MCIvPgo8c3RvcCBvZmZzZXQ9IjAuMDkzNzUiIHN0b3AtY29sb3I9IiNGNkM1OTIiLz4KPHN0b3Agb2Zmc2V0PSIwLjIwNSIgc3RvcC1jb2xvcj0iI0VCQjZBMiIvPgo8c3RvcCBvZmZzZXQ9IjAuMzI0NDY2IiBzdG9wLWNvbG9yPSIjREZBNUFGIi8+CjxzdG9wIG9mZnNldD0iMC40Mjg3NSIgc3RvcC1jb2xvcj0iI0QzOTdCRSIvPgo8c3RvcCBvZmZzZXQ9IjAuNTMzNzUiIHN0b3AtY29sb3I9IiNDNDg2Q0IiLz4KPHN0b3Agb2Zmc2V0PSIwLjY0ODc1IiBzdG9wLWNvbG9yPSIjQjU3OEQ5Ii8+CjxzdG9wIG9mZnNldD0iMC43NzEyNSIgc3RvcC1jb2xvcj0iI0ExNjZFNSIvPgo8c3RvcCBvZmZzZXQ9IjAuODkxMjUiIHN0b3AtY29sb3I9IiM4QjU3RjIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNzA0Q0ZGIi8+CjwvcmFkaWFsR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg==\"\n  },\n  \"fdb141b2-5d84-443e-8a35-4698c205a502\": {\n    \"name\": \"KeePassXC\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJiIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48ZyBpZD0iYyI+PHJlY3Qgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiIHN0eWxlPSJmaWxsOm5vbmU7IHN0cm9rZS13aWR0aDowcHg7Ii8+PGcgaWQ9ImQiPjxyZWN0IHg9IjU3LjAxMTIiIHk9IjU2LjU3NzEiIHdpZHRoPSIzLjU1OCIgaGVpZ2h0PSIzNS4yMDYxIiBzdHlsZT0iZmlsbDojMGYwZjBkOyBzdHJva2Utd2lkdGg6MHB4OyIvPjxwYXRoIGQ9Im02NCwyOC40ODg1YzQuODI4NywwLDguNzY3OS0zLjk0LDguNzY3OS04Ljc2OTgsMC0uNjM1NS0uMTI3MS0xLjI3MDktLjI1NDItMS45MDY0LTIuNzk1Ni0uNTA4NC01LjU5MTEtLjg4OTctOC41MTM4LS43NjI2LTIuNzk1NiwwLTUuNzE4Mi4yNTQyLTguNTEzOC43NjI2LS4xMjcxLjYzNTUtLjI1NDIsMS4yNzEtLjI1NDIsMS45MDY0LDAsNC44Mjk3LDMuOTM5Miw4Ljc2OTgsOC43Njc5LDguNzY5OFoiIHN0eWxlPSJmaWxsOiMwZjBmMGQ7IHN0cm9rZS13aWR0aDowcHg7Ii8+PHBhdGggZD0ibTg4LjE0MzYsMjMuNjU4N2MuMzgxMiwxLjY1MjMuNTA4MywzLjE3NzUuNTA4Myw0LjgyOTcsMCw5Ljc4NjUtNS44NDUzLDE4LjMwMjEtMTQuMTA0OSwyMi4yNDIxbC0uNjM1NCwxMC40MjIsNy40OTcyLDcuNDk4OC03LjQ5NzIsNy40OTg4LDQuOTU1OCw0Ljk1NjgtNC45NTU4LDQuOTU2OC42MzU0LDkuMjc4MS0xMC41NDY5LDEwLjU0OTEtMTAuNTQ2OS0xMC41NDkxdi00NC42MTEzYy04LjM4NjctMy45NC0xNC4xMDQ5LTEyLjQ1NTYtMTQuMTA0OS0yMi4yNDIxLDAtMS42NTIzLjI1NDEtMy4xNzc1LjUwODMtNC44Mjk3LTE0LjIzMTksOC41MTU1LTIyLjg3MjgsMjMuNzY3My0yMi44NzI4LDQwLjI5LDAsMjYuMDU1LDIxLjA5MzgsNDcuMDI2MSw0Ny4wMTYzLDQ3LjAyNjEsMjYuMDQ5NiwwLDQ3LjAxNjMtMjEuMDk4Miw0Ny4wMTYzLTQ3LjAyNjEtLjEyNzEtMTYuNTIyNy04Ljc2NzktMzEuNzc0NC0yMi44NzI4LTQwLjI5WiIgc3R5bGU9ImZpbGw6IzBmMGYwZDsgc3Ryb2tlLXdpZHRoOjBweDsiLz48cGF0aCBkPSJtNjQsMy4xMDAxQzMwLjQxOTUsMy4xMDAxLDMuMDk5OSwzMC40MTk2LDMuMDk5OSw2NHMyNy4zMTk2LDYwLjksNjAuOTAwMSw2MC45LDYwLjg5OTktMjcuMzE5OCw2MC44OTk5LTYwLjlTOTcuNTgwNiwzLjEwMDEsNjQsMy4xMDAxWm0wLDExNi42NzU5Yy0zMC43NDE1LDAtNTUuNzUxNC0yNS4wMjEyLTU1Ljc1MTQtNTUuNzc1OVMzMy4yNTg2LDguMjI0MSw2NCw4LjIyNDFzNTUuNzUxMiwyNS4wMjA5LDU1Ljc1MTIsNTUuNzc1OS0yNS4wMDk3LDU1Ljc3NTktNTUuNzUxMiw1NS43NzU5WiIgc3R5bGU9ImZpbGw6IzBmMGYwZDsgc3Ryb2tlLXdpZHRoOjBweDsiLz48L2c+PC9nPjwvc3ZnPg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImUiIHgxPSI2My45OTk4IiB5MT0iLTMuMzE0IiB4Mj0iNjMuOTk5OCIgeTI9IjExOC40ODYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM0MTQxNDEiLz48c3RvcCBvZmZzZXQ9Ii4xOTYiIHN0b3AtY29sb3I9IiMzZTNlM2UiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzYTNhM2EiLz48L2xpbmVhckdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0iZyIgY3g9IjMwMi44MDE0IiBjeT0iLTM3NS41MzAxIiBmeD0iMzAyLjgwMTQiIGZ5PSItMzc1LjUzMDEiIHI9IjIwNy4yMzM5IiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC00Ni45OTkgMTg1LjUyNzUpIHNjYWxlKC4zNTM5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjY3MzMiIHN0b3AtY29sb3I9IiNmY2ZjZmMiIHN0b3Atb3BhY2l0eT0iMCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZjZmNmYyIvPjwvcmFkaWFsR3JhZGllbnQ+PHJhZGlhbEdyYWRpZW50IGlkPSJpIiBjeD0iMzExLjE0NzIiIGN5PSItMzQ4LjIzMjUiIGZ4PSIzMTEuMTQ3MiIgZnk9Ii0zNDguMjMyNSIgcj0iMTcyLjM4OTMiIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQ2Ljk5OSAxODUuNTI3NSkgc2NhbGUoLjM1MzkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMTA4OCIgc3RvcC1jb2xvcj0iIzBmMGYwZCIgc3RvcC1vcGFjaXR5PSIwIi8+PHN0b3Agb2Zmc2V0PSIuODg1NiIgc3RvcC1jb2xvcj0iIzQxNDE0MSIgc3RvcC1vcGFjaXR5PSIwIi8+PHN0b3Agb2Zmc2V0PSIuOTMzOSIgc3RvcC1jb2xvcj0iIzFlMWUxZCIgc3RvcC1vcGFjaXR5PSIuNjg0MSIvPjxzdG9wIG9mZnNldD0iLjk4NzQiIHN0b3AtY29sb3I9IiMwZjBmMGQiLz48L3JhZGlhbEdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0ibiIgY3g9IjMxNi44OTI3IiBjeT0iLTQ1MS41OTczIiBmeD0iMzE2Ljg5MjciIGZ5PSItNDUxLjU5NzMiIHI9IjMzNC44MzY0IiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC00Ni45OTkgMTE3LjkyNTYpIHNjYWxlKC4zNTM5IC4yMDI2KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzJlNmIyNiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzZhYjUzNiIvPjwvcmFkaWFsR3JhZGllbnQ+PHJhZGlhbEdyYWRpZW50IGlkPSJwIiBjeD0iMzE0LjM4NzUiIGN5PSItNDUzLjM2MyIgZng9IjMxNC4zODc1IiBmeT0iLTQ1My4zNjMiIHI9IjY1LjM5MjUiIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQ2Ljk5OSAxODUuNTI3NSkgc2NhbGUoLjM1MzkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjNmFiNTM2Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMmU2YjI2Ii8+PC9yYWRpYWxHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9InIiIGN4PSIzMjIuMjEyOCIgY3k9Ii0zOTAuMjU0NyIgZng9IjMyMi4yMTI4IiBmeT0iLTM5MC4yNTQ3IiByPSI5Ni41ODUxIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC00NC4yMTk5IDI1Ni41NzUxKSBzY2FsZSguMzM1NiAuNTU3MikiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMwMDAiLz48c3RvcCBvZmZzZXQ9Ii43ODQyIiBzdG9wLWNvbG9yPSIjNGY0ZjRmIiBzdG9wLW9wYWNpdHk9IjAiLz48L3JhZGlhbEdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0idSIgY3g9IjMxMi45OTQ2IiBjeT0iLTQ3My4wMTc4IiBmeD0iMzEyLjk5NDYiIGZ5PSItNDczLjAxNzgiIHI9IjIyMy41MTAxIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC00Ni45OTkgMTg1LjUyNzUpIHNjYWxlKC4zNTM5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2Y1ZjVmNSIvPjxzdG9wIG9mZnNldD0iLjU0ODEiIHN0b3AtY29sb3I9IiNmMmYyZjIiLz48L3JhZGlhbEdyYWRpZW50PjwvZGVmcz48ZyBzdHlsZT0iaXNvbGF0aW9uOmlzb2xhdGU7Ij48ZyBpZD0iYiI+PGcgaWQ9ImMiPjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiBzdHlsZT0iZmlsbDpub25lOyBzdHJva2Utd2lkdGg6MHB4OyIvPjxnIGlkPSJkIj48cGF0aCBpZD0iZiIgZD0ibTYzLjk5OTksMTI0LjljLTMzLjU4MDUsMC02MC45MDAxLTI3LjMxOTgtNjAuOTAwMS02MC45UzMwLjQxOTQsMy4xLDYzLjk5OTksMy4xczYwLjg5OTksMjcuMzE5NSw2MC44OTk5LDYwLjktMjcuMzE5Myw2MC45LTYwLjg5OTksNjAuOVptMC0xMTYuNjc1OWMtMzAuNzQxNSwwLTU1Ljc1MTUsMjUuMDIxLTU1Ljc1MTUsNTUuNzc1OXMyNS4wMSw1NS43NzU5LDU1Ljc1MTUsNTUuNzc1OSw1NS43NTEyLTI1LjAyMTIsNTUuNzUxMi01NS43NzU5Uzk0Ljc0MTQsOC4yMjQxLDYzLjk5OTksOC4yMjQxWiIgc3R5bGU9ImZpbGw6dXJsKCNlKTsgc3Ryb2tlLXdpZHRoOjBweDsiLz48cGF0aCBpZD0iaCIgZD0ibTYzLjk5OTksMy4xMjM4QzMwLjQzMjUsMy4xMjM4LDMuMTIzNiwzMC40MzI3LDMuMTIzNiw2NHMyNy4zMDg5LDYwLjg3NjIsNjAuODc2Myw2MC44NzYyLDYwLjg3NjEtMjcuMzA5MSw2MC44NzYxLTYwLjg3NjJTOTcuNTY3NCwzLjEyMzgsNjMuOTk5OSwzLjEyMzhaIiBzdHlsZT0iZmlsbDp1cmwoI2cpOyBtaXgtYmxlbmQtbW9kZTpsaWdodGVuOyBvcGFjaXR5Oi4zMjsgc3Ryb2tlLXdpZHRoOjBweDsiLz48cGF0aCBpZD0iaiIgZD0ibTYzLjk5OTksMTI0LjljLTMzLjU4MDUsMC02MC45MDAxLTI3LjMxOTgtNjAuOTAwMS02MC45UzMwLjQxOTQsMy4xLDYzLjk5OTksMy4xczYwLjg5OTksMjcuMzE5NSw2MC44OTk5LDYwLjktMjcuMzE5Myw2MC45LTYwLjg5OTksNjAuOVptMC0xMTYuNjc1OWMtMzAuNzQxNSwwLTU1Ljc1MTUsMjUuMDIxLTU1Ljc1MTUsNTUuNzc1OXMyNS4wMSw1NS43NzU5LDU1Ljc1MTUsNTUuNzc1OSw1NS43NTEyLTI1LjAyMTIsNTUuNzUxMi01NS43NzU5Uzk0Ljc0MTQsOC4yMjQxLDYzLjk5OTksOC4yMjQxWiIgc3R5bGU9ImZpbGw6dXJsKCNpKTsgb3BhY2l0eTouNDQ7IHN0cm9rZS13aWR0aDowcHg7Ii8+PHBhdGggaWQ9ImsiIGQ9Im02My45OTk5LDguNTUwNGMtMzAuOTc4MywwLTU2LjE4MDksMjUuMjAyNS01Ni4xODA5LDU2LjE4MDhzMjUuMjAyNSw1Ni4xODA4LDU2LjE4MDksNTYuMTgwOCw1Ni4xODA3LTI1LjIwMjgsNTYuMTgwNy01Ni4xODA4Uzk0Ljk3ODMsOC41NTA0LDYzLjk5OTksOC41NTA0WiIgc3R5bGU9ImZpbGw6cmdiYSgxNSwxNSwxMywuMzUpOyBzdHJva2Utd2lkdGg6MHB4OyIvPjxwYXRoIGlkPSJsIiBkPSJtNjMuOTk5OSwxMjQuOWMtMzMuNTgwNSwwLTYwLjkwMDEtMjcuMzE5OC02MC45MDAxLTYwLjlTMzAuNDE5NCwzLjEsNjMuOTk5OSwzLjFzNjAuODk5OSwyNy4zMTk1LDYwLjg5OTksNjAuOS0yNy4zMTkzLDYwLjktNjAuODk5OSw2MC45Wm0wLTEyMC40OTY2QzMwLjQxOTUsNC40MDM0LDMuMDk5OCwzMS4zNTk2LDMuMDk5OCw2NC40OTMyczI3LjMxOTcsNjAuMDg5OCw2MC45MDAxLDYwLjA4OTgsNjAuODk5OS0yNi45NTY1LDYwLjg5OTktNjAuMDg5OFM5Ny41ODA0LDQuNDAzNCw2NCw0LjQwMzRaIiBzdHlsZT0iZmlsbDojMGYwZjBkOyBvcGFjaXR5Oi4zNTsgc3Ryb2tlLXdpZHRoOjBweDsiLz48L2c+PGcgaWQ9Im0iPjxwYXRoIGlkPSJvIiBkPSJtNjMuOTk5OSw4LjIyNDFjLTMwLjc1NTEsMC01NS43NzYsMjUuMDIwOS01NS43NzYsNTUuNzc1OXMyNS4wMjA5LDU1Ljc3NTksNTUuNzc2LDU1Ljc3NTksNTUuNzc1OC0yNS4wMjExLDU1Ljc3NTgtNTUuNzc1OVM5NC43NTUsOC4yMjQxLDYzLjk5OTksOC4yMjQxWiIgc3R5bGU9ImZpbGw6dXJsKCNuKTsgc3Ryb2tlLXdpZHRoOjBweDsiLz48cGF0aCBpZD0icSIgZD0ibTYzLjk5OTgsMTIuODYxM2MtOS4xNjA4LDAtMTYuNjEzNiw3LjQ1MjgtMTYuNjEzNiwxNi42MTM1czcuNDUyOCwxNi42MTM1LDE2LjYxMzYsMTYuNjEzNSwxNi42MTM1LTcuNDUyOCwxNi42MTM1LTE2LjYxMzUtNy40NTI3LTE2LjYxMzUtMTYuNjEzNS0xNi42MTM1WiIgc3R5bGU9ImZpbGw6dXJsKCNwKTsgbWl4LWJsZW5kLW1vZGU6bGlnaHRlbjsgb3BhY2l0eTouNzsgc3Ryb2tlLXdpZHRoOjBweDsiLz48cGF0aCBpZD0icyIgZD0ibTM0LjY4NzEsMTYuMzY0NWMtLjAwMjcuMDE5MS0uMDA1NC4wMzc4LS4wMDgxLjA1NjktLjA3MjUuMDEzNi0uMDczNi0uMDAzNy4wMDgxLS4wNTY5Wm00Mi4xMTc3LDM1LjgwMDFsLS43NzE0LDEzLjc4MjIsOS4xMDIzLDkuOTE2NS05LjEwMjMsOS45MTY1LDYuMDE2OCw2LjU1NDktNi4wMTY4LDYuNTU0OS43NzE0LDEzLjY2OTZoLTI1LjYxdi02MC4zOTQ2Yy0xMC4xODIzLTUuMjEwNC0xNy4xMjQ4LTE2LjQ3MTQtMTcuMTI0OC0yOS40MTMyLDAtMi4xNjU5LjMwMzItNC4xNjcxLjYwOS02LjMzLDEuMTA0Ni0uMjA4MiwxOC45NjczLTcuNjk0NywxOC45NjczLTcuNjk0Ny0uMTQ4My44MDkyLDIwLjg1NTEuODA5MiwyMC43MDY4LDAsMCwwLDE3LjI1NzcsNy40MjU3LDE4Ljk4OTEsNy43ODk5LjQzOCwyLjEyNjQuNTg3Myw0LjEwMS41ODczLDYuMjM0OCwwLDEyLjk0MTgtNy4wOTY4LDI0LjIwMjktMTcuMTI0OCwyOS40MTMzWm0xNi41MDc3LTM1LjgwMDFjLjAxMDguMDUxMS4wMTk0LjEwMTEuMDI5OS4xNTIxLjE4OTYuMDM5OS4xOTYtLjAwMzUtLjAyOTktLjE1MjFaIiBzdHlsZT0iZmlsbDp1cmwoI3IpOyBtaXgtYmxlbmQtbW9kZTptdWx0aXBseTsgb3BhY2l0eTouMzsgc3Ryb2tlLXdpZHRoOjBweDsiLz48cGF0aCBpZD0idCIgZD0ibTM0LjExNjEsMTAwLjQzMTljLTkuODk3Ny04LjYwODEtMTYuMTY0LTIxLjI5NjktMTYuMTY0LTM1LjQ5NjgsMC0xNi41MjI3LDguNjQwOS0zMS43NzQ0LDIyLjg3MjgtNDAuMjktLjI1NDEsMS42NTIzLS41MDgzLDMuMTc3NC0uNTA4Myw0LjgyOTcsMCw5Ljc4NjUsNS43MTgyLDE4LjMwMiwxNC4xMDQ5LDIyLjI0MjF2NDQuNjExM2wxMC41NDY5LDEwLjU0OTEsMTAuNTQ2OS0xMC41NDkxLS42MzU0LTkuMjc4MSw0Ljk1NTgtNC45NTY4LTQuOTU1OC00Ljk1NjgsNy40OTcyLTcuNDk4OC03LjQ5NzItNy40OTg4LjYzNTQtMTAuNDIyYzguMjU5Ni0zLjk0LDE0LjEwNDktMTIuNDU1NiwxNC4xMDQ5LTIyLjI0MjEsMC0xLjY1MjMtLjEyNzEtMy4xNzc0LS41MDgzLTQuODI5Nyw1LjE2NDMsMy4xMTc4LDkuNTkyMiw3LjE0MTYsMTMuMTQxOSwxMS43OTcyLTguNTg0MS0xMS43ODg5LTIyLjUyOTMtMTkuNDU4Ny0zOC4yNzY2LTE5LjQ1ODctMjYuMDkxMSwwLTQ3LjI0MjEsMjEuMDUtNDcuMjQyMSw0Ny4wMTY1LDAsMTQuNjkzLDYuNzczNiwyNy44MTAxLDE3LjM4MDksMzYuNDMxOFptMjcuNDIxNC03LjY2MjRoLTMuNTU4di0zNS4yMDYxaDMuNTU4djM1LjIwNjFabS01LjA4MjgtNzMuOTcwOGMyLjc5NTYtLjUwODQsNS43MTgyLS43NjI2LDguNTEzOC0uNzYyNiwyLjkyMjYtLjEyNzEsNS43MTgyLjI1NDIsOC41MTM4Ljc2MjYuMTI3MS42MzU1LjI1NDIsMS4yNzEuMjU0MiwxLjkwNjQsMCw0LjgyOTctMy45MzkyLDguNzY5OC04Ljc2NzksOC43Njk4cy04Ljc2NzktMy45NC04Ljc2NzktOC43Njk4YzAtLjYzNTUuMTI3MS0xLjI3MDkuMjU0Mi0xLjkwNjRaIiBzdHlsZT0iZmlsbDojMGYwZjBkOyBtaXgtYmxlbmQtbW9kZTptdWx0aXBseTsgb3BhY2l0eTouMjsgc3Ryb2tlLXdpZHRoOjBweDsiLz48cGF0aCBpZD0idiIgZD0ibTYzLjk5OTksOC4yMjQxYy0zMC43NDE1LDAtNTUuNzUxNCwyNS4wMjA5LTU1Ljc1MTQsNTUuNzc1OXMyNS4wMSw1NS43NzU5LDU1Ljc1MTQsNTUuNzc1OSw1NS43NTEyLTI1LjAyMTIsNTUuNzUxMi01NS43NzU5Uzk0Ljc0MTQsOC4yMjQxLDYzLjk5OTksOC4yMjQxWm0tOC41MTM4LDkuNTg4MmMyLjc5NTYtLjUwODQsNS43MTgyLS43NjI2LDguNTEzOC0uNzYyNiwyLjkyMjYtLjEyNzEsNS43MTgyLjI1NDIsOC41MTM4Ljc2MjYuMTI3MS42MzU1LjI1NDIsMS4yNzEuMjU0MiwxLjkwNjQsMCw0LjgyOTctMy45MzkyLDguNzY5OC04Ljc2NzksOC43Njk4cy04Ljc2NzktMy45NC04Ljc2NzktOC43Njk4YzAtLjYzNTUuMTI3MS0xLjI3MDkuMjU0Mi0xLjkwNjRabTUuMDgyOCwzOC43NjQ4djM1LjIwNjFoLTMuNTU4di0zNS4yMDYxaDMuNTU4Wm0zLjQzMDksNTQuMzk3OGMtMjUuOTIyNSwwLTQ3LjAxNjMtMjAuOTcxMS00Ny4wMTYzLTQ3LjAyNjEsMC0xNi41MjI3LDguNjQwOS0zMS43NzQ0LDIyLjg3MjgtNDAuMjktLjI1NDEsMS42NTIzLS41MDgzLDMuMTc3NC0uNTA4Myw0LjgyOTcsMCw5Ljc4NjUsNS43MTgyLDE4LjMwMiwxNC4xMDQ5LDIyLjI0MjF2NDQuNjExM2wxMC41NDY5LDEwLjU0OTEsMTAuNTQ2OS0xMC41NDkxLS42MzU0LTkuMjc4MSw0Ljk1NTgtNC45NTY4LTQuOTU1OC00Ljk1NjgsNy40OTcyLTcuNDk4OC03LjQ5NzItNy40OTg4LjYzNTQtMTAuNDIyYzguMjU5Ni0zLjk0LDE0LjEwNDktMTIuNDU1NiwxNC4xMDQ5LTIyLjI0MjEsMC0xLjY1MjMtLjEyNzEtMy4xNzc0LS41MDgzLTQuODI5NywxNC4xMDQ5LDguNTE1NiwyMi43NDU3LDIzLjc2NzMsMjIuODcyOCw0MC4yOSwwLDI1LjkyNzktMjAuOTY2OCw0Ny4wMjYxLTQ3LjAxNjMsNDcuMDI2MVoiIHN0eWxlPSJmaWxsOnVybCgjdSk7IHN0cm9rZS13aWR0aDowcHg7Ii8+PC9nPjwvZz48L2c+PC9nPjwvc3ZnPg==\"\n  },\n  \"cc45f64e-52a2-451b-831a-4edd8022a202\": {\n    \"name\": \"ToothPic Passkey Provider\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgdmlld0JveD0iMCAwIDY2Ni42NjY2OSA2NjYuNjY2NjkiCiAgIGhlaWdodD0iNjY2LjY2NjY5IgogICB3aWR0aD0iNjY2LjY2NjY5IgogICB4bWw6c3BhY2U9InByZXNlcnZlIgogICBpZD0ic3ZnMiIKICAgdmVyc2lvbj0iMS4xIgogICBzb2RpcG9kaTpkb2NuYW1lPSJsb2dvUXVhZHJhdG9fMDMuc3ZnIgogICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9ImxvZ29RdWFkcmF0b19mb25kb190cmFzcGFyZW50ZS5zdmciCiAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0ODAuNTQiCiAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0ODAuNTQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPjxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0ibmFtZWR2aWV3MSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnNob3dwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZWNoZWNrZXJib2FyZD0iMCIKICAgICBpbmtzY2FwZTpkZXNrY29sb3I9IiNkMWQxZDEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIiAvPjxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTgiPjxyZGY6UkRGPjxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj48ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD48ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+PC9jYzpXb3JrPjwvcmRmOlJERj48L21ldGFkYXRhPjxkZWZzCiAgICAgaWQ9ImRlZnM2Ij48Y2xpcFBhdGgKICAgICAgIGlkPSJjbGlwUGF0aDIwIgogICAgICAgY2xpcFBhdGhVbml0cz0idXNlclNwYWNlT25Vc2UiPjxwYXRoCiAgICAgICAgIGlkPSJwYXRoMTgiCiAgICAgICAgIGQ9Ik0gMCw1MDAgSCA1MDAgViAwIEggMCBaIiAvPjwvY2xpcFBhdGg+PC9kZWZzPjxnCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4zMzMzMzMzLDAsMCwtMS4zMzMzMzMzLDAsNjY2LjY2NjY3KSIKICAgICBpZD0iZzEwIj48ZwogICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoMjApIgogICAgICAgICBpZD0iZzE2Ij48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI3MS4yOTM1LDE2MC4zODc3KSIKICAgICAgICAgICBpZD0iZzIyIj48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGgyNCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBkPSJtIDAsMCBjIC0xLjYwOSwyMi4zMjUgOC4xOTEsMzUuOTYxIDI3Ljk2OSw0NS43MjIgMzguMjUxLDE4Ljg3OCA2MC42NzcsNDkuOTA2IDY1Ljk1NCw5Mi43NTYgNS44NjksNDcuNjY0IC0xOS4wMTIsOTQuNDExIC02Mi41NTUsMTE3LjE3MyAtNDMuMDg2LDIyLjUyMyAtOTYuMjMsMTUuNzI2IC0xMzEuNjM5LC0xNi44MzYgLTYyLjY2NCwtNTcuNjI3IC00OS4xNTcsLTE1NS4wMjcgMjcuMzc4LC0xOTIuOTc1IDIwLjM2OSwtMTAuMSAyOS41MTYsLTI0LjQwOSAyOC44MjcsLTQ3LjA1MSBsIC0wLjY2NSwtOTAuOTU3IGMgLTAuMTU0LC01LjIxOSAtMC4xMDYsLTE1LjE2MyAtOS4yNzMsLTE1LjE2MyAtMTAuMTUxLDAgLTkuMzc3LDkuODYzIC05LjU0LDE1LjA1NSBsIC0wLjIsODkuNDYyIGMgMC41MjksMTUuOTY5IC00LjczMywyNS4yMjIgLTE5LjU0OCwzMi40NTcgLTU0LjA2NCwyNi4zOTkgLTgxLjc2NSw4MS4yMTggLTc0LjA4NSwxNDMuMjM5IDYuNTQ3LDUyLjg4MiA1MC40ODksOTkuMjA0IDEwNS45MzcsMTExLjY3OCBDIDE0LjcyMywyOTkuNDQyIDgyLjAwOCwyNjIuNzcxIDEwNS4zMzIsMTk5LjExOCAxMjkuMTI0LDEzNC4xODYgMTAyLjYxNSw2Mi4xNzMgNDEuNjQ3LDMxLjMxNCAyMy43OTUsMjIuMjc5IDE3LjE2MiwxMS4zODUgMTguMjM2LC04LjE0MyBsIDAuMzIyLC04Mi4wMDUgYyAtMC4wNCwtNy4wNTEgMS41MTcsLTE3LjQxOCAtOC43MywtMTcuMTEzIC0xMC44MDgsMC4zMjIgLTkuNDI1LDEwLjM4OCAtOS41NjMsMTcuNzkxIHoiIC8+PC9nPjxnCiAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzUzLjc0ODUsMzEyLjY1MTQpIgogICAgICAgICAgIGlkPSJnMjYiPjxwYXRoCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIKICAgICAgICAgICAgIGQ9Im0gMCwwIGMgLTEuODc3LC00NC41MjUgLTIxLjQ4OCwtNzYuNTQ5IC02MC45NSwtOTUuNjg2IC02LjY2OSwtMy4yMzQgLTE0LjMxMSwtOC41ODkgLTIwLjM4MiwwLjY2NyAtMy41OSwxMS4wMTIgNC44NTMsMTMuMDIgMTEuOTQ3LDE2LjQ2NSAzMy44MjMsMTYuNDIyIDUzLjIwOSw0OS42NjUgNTAuMDM0LDg1LjU2MSAtMi45NzYsMzMuNjM2IC0yNy44NTEsNjQuMDczIC02MC40MzMsNzMuOTQ0IC00Mi40MjIsMTIuODUzIC04Ni45NDIsLTguMjc0IC0xMDQuMDQ0LC00OS4zNzMgLTE2LjkxNiwtNDAuNjU1IDAuMTg4LC04Ny41ODcgMzkuNDczLC0xMDguMTY1IDMuNTAxLC0xLjgzNSA3LjI2OCwtMy42ODggMTAuNTI1LC01LjE5NCA0LjgxNCwtMi4yMjYgOS4yNzMsLTYuNjQxIDYuMjE0LC0xMy4wOTcgLTMuNzczLC01Ljk0OSAtMTAuMTU4LC00LjkyOCAtMTMuODMsLTMuNTczIC0zNC4wNSwxMi41NTggLTU2LjE0OCwzNy4wOTQgLTY1LjA1Nyw3MS45NTMgLTE0LjQzLDU2LjQ1NiAyMC4zMjIsMTEzLjUzNiA3Ni4zMSwxMjcuNTE0IEMgLTczLjQ3OSwxMTUuMTc1IC0xNi41MzMsODAuMjcgLTIuNzc2LDIyLjYxMyAtMC45NDksMTQuOTU4IC0wLjgwOCw2LjkgMCwwIiAvPjwvZz48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMwMi4zMDYyLDMxMi4xMjc5KSIKICAgICAgICAgICBpZD0iZzMwIj48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGgzMiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBkPSJtIDAsMCBjIC0wLjEyNywzMC4wMzEgLTI0LjYxLDU0LjMxMyAtNTQuMTM0LDUzLjY4NyAtMjguNzAxLC0wLjYwOSAtNTMuMTU1LC0yNS4wOTcgLTUzLjUxNSwtNTMuNTkxIC0wLjM2NywtMjguOTggMjQuODU4LC01NC4yMzYgNTQuMTUxLC01NC4yMiBDIC0yMy40NCwtNTQuMTA3IDAuMTI4LC0zMC4yNjMgMCwwIG0gLTUyLjA2OSw3Mi42NDkgYyAzOS44MTEsLTAuNDM4IDcyLjYyNywtMzQuMDk3IDcyLjA0NiwtNzMuODk2IC0wLjU5MiwtNDAuNTI0IC0zNC41MDMsLTczLjAyNCAtNzUuMzYzLC03Mi4yMjYgLTM5Ljg4OCwwLjc4IC03MS4wNzcsMzMuNzc4IC03MC42OCw3NC43ODEgMC4zODksNDAuMTU2IDMzLjIwMiw3MS43OTIgNzMuOTk3LDcxLjM0MSIgLz48L2c+PGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMzkuNTg3OSwyMTIuNzQ0MSkiCiAgICAgICAgICAgaWQ9ImczNCI+PHBhdGgKICAgICAgICAgICAgIGlkPSJwYXRoMzYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIgogICAgICAgICAgICAgZD0ibSAwLDAgYyAwLjIxNCw1LjQxMSAtMS43MjYsMTUuOSA5LjA1LDE1LjkgMTAuMDQ4LDAgOS40NDQsLTEwLjIzNSA5LjUxOSwtMTUuNDIgbCAtMC4wMTUsLTE4MS4yODIgYyAtMC4wNzcsLTUuMzY0IDAuNTQ0LC0xNS4xNzUgLTkuMDk1LC0xNS4xNDkgLTkuODMxLC0wLjU2IC0xMC4wMDEsOC4zMjMgLTkuOTUyLDE2LjE2NSB6IiAvPjwvZz48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE3OS42MTMzLDQ3NC4yOTQ5KSIKICAgICAgICAgICBpZD0iZzM4Ij48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjE3LjkwMjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgICAgICBkPSJNIDAsMCBDIC00Ny44MzksMCAtODQuNTc1LC0zNS45NjEgLTg0LjU3NSwtODIuNTYxIiAvPjwvZz48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQwNi4zNjgyLDM5MS43MzQ0KSIKICAgICAgICAgICBpZD0iZzQyIj48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg0NCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjE3LjkwMjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgICAgICBkPSJNIDAsMCBDIDAsNTMuMTg0IC00MC42MSw4Mi41NjEgLTg0LjU3NSw4Mi41NjEiIC8+PC9nPjxnCiAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzIxLjc5MywyNC44NjIzKSIKICAgICAgICAgICBpZD0iZzQ2Ij48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg0OCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjE3LjkwMjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgICAgICBkPSJNIDAsMCBDIDQ3LjgzOSwwIDg0LjU3NSwzNS45NjIgODQuNTc1LDgyLjU2MSIgLz48L2c+PGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5NS4wMzg2LDEwNy40MjI5KSIKICAgICAgICAgICBpZD0iZzUwIj48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg1MiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjE3LjkwMjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgICAgICBkPSJNIDAsMCBDIDAsLTUzLjE4MyA0MC42MSwtODIuNTYxIDg0LjU3NSwtODIuNTYxIiAvPjwvZz48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI3Mi41MzkxLDMxMS43NTQ5KSIKICAgICAgICAgICBpZD0iZzU0Ij48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg1NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNmNjg3MTI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBkPSJtIDAsMCBjIDAsLTEzLjAxOSAtMTAuNTUzLC0yMy41NzIgLTIzLjU3MiwtMjMuNTcyIC0xMy4wMTksMCAtMjMuNTczLDEwLjU1MyAtMjMuNTczLDIzLjU3MiAwLDEzLjAxOSAxMC41NTQsMjMuNTcyIDIzLjU3MywyMy41NzIgQyAtMTAuNTUzLDIzLjU3MiAwLDEzLjAxOSAwLDAiIC8+PC9nPjxnCiAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjQ4Ljk2NjgsMjg4LjE4MjYpIgogICAgICAgICAgIGlkPSJnNTgiPjxwYXRoCiAgICAgICAgICAgICBpZD0icGF0aDYwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIKICAgICAgICAgICAgIGQ9Im0gMCwwIGMgLTEzLjAxOSwwIC0yMy41NzIsMTAuNTU0IC0yMy41NzIsMjMuNTcyIDAsMTMuMDE5IDEwLjU1MywyMy41NzMgMjMuNTcyLDIzLjU3MyAxMy4wMTksMCAyMy41NzIsLTEwLjU1NCAyMy41NzIsLTIzLjU3MyBDIDIzLjU3MiwxMC41NTQgMTMuMDE5LDAgMCwwIE0gMS40MDIsNjQuNDUxIEMgLTIyLjAxNyw2NC44ODQgLTQwLjY1OSw0Ny4zMzUgLTQxLjIxMywyNC4zMzYgLTQxLjc2LDEuNjI2IC0yMy4yNDgsLTE3LjE0MyAtMC4xMzIsLTE3LjMxMyAyMy42MTQsLTE3LjQ4OSA0MS4yMjksLTAuMDk0IDQxLjIyNSwyMy41MyA0MS4yMjIsNDYuNjIgMjQuMjgsNjQuMDI5IDEuNDAyLDY0LjQ1MSIgLz48L2c+PC9nPjwvZz48L2c+PC9zdmc+Cg==\",\n    \"icon_light\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmlld0JveD0iMCAwIDY2Ni42NjY2OSA2NjYuNjY2NjkiCiAgIGhlaWdodD0iNjY2LjY2NjY5IgogICB3aWR0aD0iNjY2LjY2NjY5IgogICB4bWw6c3BhY2U9InByZXNlcnZlIgogICBpZD0ic3ZnMiIKICAgdmVyc2lvbj0iMS4xIj48bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE4Ij48cmRmOlJERj48Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+PGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+PGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPjwvY2M6V29yaz48L3JkZjpSREY+PC9tZXRhZGF0YT48ZGVmcwogICAgIGlkPSJkZWZzNiI+PGNsaXBQYXRoCiAgICAgICBpZD0iY2xpcFBhdGgxOCIKICAgICAgIGNsaXBQYXRoVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48cGF0aAogICAgICAgICBpZD0icGF0aDE2IgogICAgICAgICBkPSJNIDAsNTAwIEggNTAwIFYgMCBIIDAgWiIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMzMzMzMzMywwLDAsLTEuMzMzMzMzMywwLDY2Ni42NjY2NykiCiAgICAgaWQ9ImcxMCI+PGcKICAgICAgIGlkPSJnMTIiPjxnCiAgICAgICAgIGNsaXAtcGF0aD0idXJsKCNjbGlwUGF0aDE4KSIKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNzEuOTMwMiwxNjEuOTg2OCkiCiAgICAgICAgICAgaWQ9ImcyMCI+PHBhdGgKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojMjMxZjIwO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIgogICAgICAgICAgICAgZD0ibSAwLDAgYyAtMS42MDksMjIuMzI1IDguMTkxLDM1Ljk2MSAyNy45NjksNDUuNzIyIDM4LjI1MSwxOC44NzggNjAuNjc3LDQ5LjkwNiA2NS45NTQsOTIuNzU2IDUuODcsNDcuNjY0IC0xOS4wMTIsOTQuNDExIC02Mi41NTUsMTE3LjE3MyAtNDMuMDg2LDIyLjUyMyAtOTYuMjMsMTUuNzI2IC0xMzEuNjM5LC0xNi44MzYgLTYyLjY2NCwtNTcuNjI2IC00OS4xNTYsLTE1NS4wMjYgMjcuMzc4LC0xOTIuOTc1IDIwLjM2OSwtMTAuMDk5IDI5LjUxNiwtMjQuNDA5IDI4LjgyNywtNDcuMDUxIGwgLTAuNjY1LC05MC45NTYgYyAtMC4xNTQsLTUuMjIgLTAuMTA1LC0xNS4xNjQgLTkuMjcyLC0xNS4xNjQgLTEwLjE1MiwwIC05LjM3OCw5Ljg2NCAtOS41NDEsMTUuMDU1IGwgLTAuMiw4OS40NjEgYyAwLjUyOSwxNS45NzEgLTQuNzMzLDI1LjIyMyAtMTkuNTQ4LDMyLjQ1OCAtNTQuMDY0LDI2LjM5OSAtODEuNzY0LDgxLjIxOCAtNzQuMDg1LDE0My4yMzkgNi41NDcsNTIuODgyIDUwLjQ4OSw5OS4yMDQgMTA1LjkzNywxMTEuNjc4IEMgMTQuNzIzLDI5OS40NDMgODIuMDA4LDI2Mi43NzIgMTA1LjMzMiwxOTkuMTE4IDEyOS4xMjQsMTM0LjE4NiAxMDIuNjE1LDYyLjE3MyA0MS42NDcsMzEuMzE0IDIzLjc5NiwyMi4yNzkgMTcuMTYyLDExLjM4NSAxOC4yMzYsLTguMTQyIGwgMC4zMjIsLTgyLjAwNiBjIC0wLjA0LC03LjA1MSAxLjUxOCwtMTcuNDE4IC04LjczLC0xNy4xMTMgLTEwLjgwOCwwLjMyMiAtOS40MjUsMTAuMzg4IC05LjU2MywxNy43OTEgeiIgLz48L2c+PGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNTQuMzg1MywzMTQuMjUxKSIKICAgICAgICAgICBpZD0iZzI0Ij48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiMyMzFmMjA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBkPSJtIDAsMCBjIC0xLjg3NywtNDQuNTI1IC0yMS40ODgsLTc2LjU0OSAtNjAuOTUsLTk1LjY4NiAtNi42NjksLTMuMjM0IC0xNC4zMTEsLTguNTkgLTIwLjM4MiwwLjY2NyAtMy41OSwxMS4wMTIgNC44NTMsMTMuMDE5IDExLjk0OCwxNi40NjUgMzMuODIyLDE2LjQyMiA1My4yMDgsNDkuNjY1IDUwLjAzMyw4NS41NjEgLTIuOTc2LDMzLjYzNSAtMjcuODUxLDY0LjA3MyAtNjAuNDMzLDczLjk0NCAtNDIuNDIyLDEyLjg1MiAtODYuOTQyLC04LjI3NSAtMTA0LjA0NCwtNDkuMzc0IC0xNi45MTYsLTQwLjY1NCAwLjE4OCwtODcuNTg2IDM5LjQ3MywtMTA4LjE2NCAzLjUwMSwtMS44MzUgNy4yNjgsLTMuNjg5IDEwLjUyNSwtNS4xOTUgNC44MTQsLTIuMjI1IDkuMjczLC02LjY0IDYuMjE0LC0xMy4wOTYgLTMuNzczLC01Ljk0OSAtMTAuMTU4LC00LjkyOCAtMTMuODMsLTMuNTc0IC0zNC4wNSwxMi41NTkgLTU2LjE0OCwzNy4wOTQgLTY1LjA1Nyw3MS45NTMgLTE0LjQzLDU2LjQ1NyAyMC4zMjIsMTEzLjUzNyA3Ni4zMSwxMjcuNTE1IEMgLTczLjQ3OSwxMTUuMTc0IC0xNi41MzMsODAuMjcgLTIuNzc2LDIyLjYxMyAtMC45NDksMTQuOTU4IC0wLjgwOCw2LjkgMCwwIiAvPjwvZz48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMwMi45NDI5LDMxMy43Mjc1KSIKICAgICAgICAgICBpZD0iZzI4Ij48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGgzMCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiMyMzFmMjA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBkPSJNIDAsMCBDIC0wLjEyNiwzMC4wMzEgLTI0LjYxLDU0LjMxMiAtNTQuMTM0LDUzLjY4NyAtODIuODM1LDUzLjA3OCAtMTA3LjI4OSwyOC41ODkgLTEwNy42NDksMC4wOTUgLTEwOC4wMTYsLTI4Ljg4NCAtODIuNzkxLC01NC4xNCAtNTMuNDk3LC01NC4xMjQgLTIzLjQ0LC01NC4xMDcgMC4xMjgsLTMwLjI2MyAwLDAgbSAtNTIuMDY5LDcyLjY0OSBjIDM5LjgxMSwtMC40MzkgNzIuNjI3LC0zNC4wOTggNzIuMDQ2LC03My44OTcgLTAuNTkyLC00MC41MjQgLTM0LjUwMywtNzMuMDIzIC03NS4zNjMsLTcyLjIyNSAtMzkuODg4LDAuNzc5IC03MS4wNzcsMzMuNzc3IC03MC42OCw3NC43OCAwLjM4OSw0MC4xNTYgMzMuMjAyLDcxLjc5MyA3My45OTcsNzEuMzQyIiAvPjwvZz48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI0MC4yMjQ2LDIxNC4zNDMzKSIKICAgICAgICAgICBpZD0iZzMyIj48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGgzNCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiMyMzFmMjA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBkPSJtIDAsMCBjIDAuMjE0LDUuNDExIC0xLjcyNiwxNS45IDkuMDUsMTUuOSAxMC4wNDksMCA5LjQ0NCwtMTAuMjM1IDkuNTE5LC0xNS40MiBsIC0wLjAxNCwtMTgxLjI4MiBjIC0wLjA3NywtNS4zNjQgMC41NDQsLTE1LjE3NCAtOS4wOTYsLTE1LjE0OSAtOS44MzEsLTAuNTYgLTEwLjAwMSw4LjMyNCAtOS45NTIsMTYuMTY1IHoiIC8+PC9nPjxnCiAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTgwLjI1MDUsNDc1Ljg5NDUpIgogICAgICAgICAgIGlkPSJnMzYiPjxwYXRoCiAgICAgICAgICAgICBpZD0icGF0aDM4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzIzMWYyMDtzdHJva2Utd2lkdGg6MTcuOTAyMDAwNDM7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjEwO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgZD0iTSAwLDAgQyAtNDcuODM5LDAgLTg0LjU3NSwtMzUuOTYxIC04NC41NzUsLTgyLjU2MSIgLz48L2c+PGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0MDcuMDA0OSwzOTMuMzM0KSIKICAgICAgICAgICBpZD0iZzQwIj48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg0MiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMyMzFmMjA7c3Ryb2tlLXdpZHRoOjE3LjkwMjAwMDQzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgICAgIGQ9Ik0gMCwwIEMgMCw1My4xODMgLTQwLjYxLDgyLjU2MSAtODQuNTc1LDgyLjU2MSIgLz48L2c+PGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMjIuNDMwMiwyNi40NjE5KSIKICAgICAgICAgICBpZD0iZzQ0Ij48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMyMzFmMjA7c3Ryb2tlLXdpZHRoOjE3LjkwMjAwMDQzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgICAgIGQ9Ik0gMCwwIEMgNDcuODM5LDAgODQuNTc1LDM1Ljk2MSA4NC41NzUsODIuNTYxIiAvPjwvZz48ZwogICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDk1LjY3NTMsMTA5LjAyMjUpIgogICAgICAgICAgIGlkPSJnNDgiPjxwYXRoCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzIzMWYyMDtzdHJva2Utd2lkdGg6MTcuOTAyMDAwNDM7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjEwO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgZD0iTSAwLDAgQyAwLC01My4xODMgNDAuNjEsLTgyLjU2MSA4NC41NzUsLTgyLjU2MSIgLz48L2c+PGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNzMuMTc2MywzMTMuMzU0KSIKICAgICAgICAgICBpZD0iZzUyIj48cGF0aAogICAgICAgICAgICAgaWQ9InBhdGg1NCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNmNjg3MTI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBkPSJtIDAsMCBjIDAsLTEzLjAxOSAtMTAuNTU0LC0yMy41NzIgLTIzLjU3MiwtMjMuNTcyIC0xMy4wMTksMCAtMjMuNTczLDEwLjU1MyAtMjMuNTczLDIzLjU3MiAwLDEzLjAxOSAxMC41NTQsMjMuNTcyIDIzLjU3MywyMy41NzIgQyAtMTAuNTU0LDIzLjU3MiAwLDEzLjAxOSAwLDAiIC8+PC9nPjxnCiAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjQ5LjYwMzUsMjg5Ljc4MTcpIgogICAgICAgICAgIGlkPSJnNTYiPjxwYXRoCiAgICAgICAgICAgICBpZD0icGF0aDU4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzIzMWYyMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIKICAgICAgICAgICAgIGQ9Im0gMCwwIGMgLTEzLjAxOSwwIC0yMy41NzIsMTAuNTU0IC0yMy41NzIsMjMuNTcyIDAsMTMuMDE5IDEwLjU1MywyMy41NzMgMjMuNTcyLDIzLjU3MyAxMy4wMTksMCAyMy41NzMsLTEwLjU1NCAyMy41NzMsLTIzLjU3MyBDIDIzLjU3MywxMC41NTQgMTMuMDE5LDAgMCwwIE0gMS40MDIsNjQuNDUyIEMgLTIyLjAxNyw2NC44ODQgLTQwLjY1OSw0Ny4zMzUgLTQxLjIxMywyNC4zMzYgLTQxLjc2LDEuNjI2IC0yMy4yNDgsLTE3LjE0MyAtMC4xMzIsLTE3LjMxMyAyMy42MTQsLTE3LjQ4OSA0MS4yMjksLTAuMDk0IDQxLjIyNSwyMy41MyA0MS4yMjIsNDYuNjIxIDI0LjI4LDY0LjAyOSAxLjQwMiw2NC40NTIiIC8+PC9nPjwvZz48L2c+PC9nPjwvc3ZnPg==\"\n  },\n  \"bfc748bb-3429-4faa-b9f9-7cfa9f3b76d0\": {\n    \"name\": \"iPasswords\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEwODAgMTA4MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxuczpzZXJpZj0iaHR0cDovL3d3dy5zZXJpZi5jb20vIiBzdHlsZT0iZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjI7Ij4KICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuOTgzODI3LDAsMCwwLjk4MzgyNywxMy42NjEsLTAuMjg0Njk5KSI+CiAgICAgICAgPHBhdGggZD0iTTcyMy4yNTMsNDkzLjcwNEM3NTYuMjg4LDQ5NS4yMiA3ODIuNjQ0LDUyMi41MiA3ODIuNjQ0LDU1NS45MjdMNzgyLjY0NCw4MzQuMzA0Qzc4Mi42NDQsODY4LjY4MyA3NTQuNzMzLDg5Ni41OTMgNzIwLjM1NSw4OTYuNTkzTDM1MS4zOTMsODk2LjU5M0MzMTcuMDE1LDg5Ni41OTMgMjg5LjEwNCw4NjguNjgzIDI4OS4xMDQsODM0LjMwNEwyODkuMTA0LDU1NS45MjdDMjg5LjEwNCw1MjMuMTE3IDMxNC41MjYsNDk2LjE5OCAzNDYuNzMsNDkzLjgxTDM0Ni43MywzOTAuMTE5QzM0Ni43MywyODYuMjE1IDQzMS4wODgsMjAxLjg1OCA1MzQuOTkyLDIwMS44NThDNjM4Ljg5NiwyMDEuODU4IDcyMy4yNTMsMjg2LjIxNSA3MjMuMjUzLDM5MC4xMTlMNzIzLjI1Myw0OTMuNzA0Wk00MzMuMzI4LDQ5MC45NjZMNjM2LjY4Niw0OTIuMTE2QzYzNi42ODYsNDkyLjExNiA2MzcuMTQyLDM4NS4xMzIgNjM3LjA4NiwzODQuNDg5QzYzMS44NDksMzI0LjE2MyA1NzkuMTE4LDI4OC4wNzkgNTM0Ljk5MiwyODguNTM1QzQ5My4wMTcsMjg4Ljk2OSA0MzUuOTIsMzE4LjEgNDMzLjY1NiwzODMuNzk3QzQzMy40NzYsMzg5LjAxNyA0MzMuMzI4LDQ5MC45NjYgNDMzLjMyOCw0OTAuOTY2Wk01MDMuMjk2LDcxNC4zODJMNDkyLjQzMyw3ODQuNDU1QzQ5Mi40MzMsNzg0LjQ1NSA0OTAuMzc2LDc5OC4yODQgNDkyLjQxNiw4MDIuNjY0QzQ5Ni43OCw4MTIuMDMxIDUwMy44MjIsODExLjExMSA1MDMuODIyLDgxMS4xMTFMNTY1LjYsODEwLjgzOEM1NjUuNiw4MTAuODM4IDU3Mi44NjMsODExLjQ3MiA1NzcuNjM3LDgwMi4xNTVDNTc5Ljg4LDc5Ny43NzUgNTc3LjMyMyw3ODMuNzQgNTc3LjMyMyw3ODMuNzRMNTY2LjY0OSw3MTQuNDAxQzU5MC43NDMsNzAyLjY0OSA2MDcuMzU5LDY3Ny45MDggNjA3LjM1OSw2NDkuMzE4QzYwNy4zNTksNjA5LjM3NyA1NzQuOTMyLDU3Ni45NSA1MzQuOTkyLDU3Ni45NUM0OTUuMDUxLDU3Ni45NSA0NjIuNjI0LDYwOS4zNzcgNDYyLjYyNCw2NDkuMzE4QzQ2Mi42MjQsNjc3Ljg5MyA0NzkuMjIzLDcwMi42MjIgNTAzLjI5Niw3MTQuMzgyWiIgc3R5bGU9ImZpbGw6cmdiKDAsMTUyLDI0OCk7Ii8+CiAgICA8L2c+Cjwvc3ZnPgo=\",\n    \"icon_light\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEwODAgMTA4MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxuczpzZXJpZj0iaHR0cDovL3d3dy5zZXJpZi5jb20vIiBzdHlsZT0iZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjI7Ij4KICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuOTgzODI3LDAsMCwwLjk4MzgyNywxMy42NjEsLTAuMjg0Njk5KSI+CiAgICAgICAgPHBhdGggZD0iTTcyMy4yNTMsNDkzLjcwNEM3NTYuMjg4LDQ5NS4yMiA3ODIuNjQ0LDUyMi41MiA3ODIuNjQ0LDU1NS45MjdMNzgyLjY0NCw4MzQuMzA0Qzc4Mi42NDQsODY4LjY4MyA3NTQuNzMzLDg5Ni41OTMgNzIwLjM1NSw4OTYuNTkzTDM1MS4zOTMsODk2LjU5M0MzMTcuMDE1LDg5Ni41OTMgMjg5LjEwNCw4NjguNjgzIDI4OS4xMDQsODM0LjMwNEwyODkuMTA0LDU1NS45MjdDMjg5LjEwNCw1MjMuMTE3IDMxNC41MjYsNDk2LjE5OCAzNDYuNzMsNDkzLjgxTDM0Ni43MywzOTAuMTE5QzM0Ni43MywyODYuMjE1IDQzMS4wODgsMjAxLjg1OCA1MzQuOTkyLDIwMS44NThDNjM4Ljg5NiwyMDEuODU4IDcyMy4yNTMsMjg2LjIxNSA3MjMuMjUzLDM5MC4xMTlMNzIzLjI1Myw0OTMuNzA0Wk00MzMuMzI4LDQ5MC45NjZMNjM2LjY4Niw0OTIuMTE2QzYzNi42ODYsNDkyLjExNiA2MzcuMTQyLDM4NS4xMzIgNjM3LjA4NiwzODQuNDg5QzYzMS44NDksMzI0LjE2MyA1NzkuMTE4LDI4OC4wNzkgNTM0Ljk5MiwyODguNTM1QzQ5My4wMTcsMjg4Ljk2OSA0MzUuOTIsMzE4LjEgNDMzLjY1NiwzODMuNzk3QzQzMy40NzYsMzg5LjAxNyA0MzMuMzI4LDQ5MC45NjYgNDMzLjMyOCw0OTAuOTY2Wk01MDMuMjk2LDcxNC4zODJMNDkyLjQzMyw3ODQuNDU1QzQ5Mi40MzMsNzg0LjQ1NSA0OTAuMzc2LDc5OC4yODQgNDkyLjQxNiw4MDIuNjY0QzQ5Ni43OCw4MTIuMDMxIDUwMy44MjIsODExLjExMSA1MDMuODIyLDgxMS4xMTFMNTY1LjYsODEwLjgzOEM1NjUuNiw4MTAuODM4IDU3Mi44NjMsODExLjQ3MiA1NzcuNjM3LDgwMi4xNTVDNTc5Ljg4LDc5Ny43NzUgNTc3LjMyMyw3ODMuNzQgNTc3LjMyMyw3ODMuNzRMNTY2LjY0OSw3MTQuNDAxQzU5MC43NDMsNzAyLjY0OSA2MDcuMzU5LDY3Ny45MDggNjA3LjM1OSw2NDkuMzE4QzYwNy4zNTksNjA5LjM3NyA1NzQuOTMyLDU3Ni45NSA1MzQuOTkyLDU3Ni45NUM0OTUuMDUxLDU3Ni45NSA0NjIuNjI0LDYwOS4zNzcgNDYyLjYyNCw2NDkuMzE4QzQ2Mi42MjQsNjc3Ljg5MyA0NzkuMjIzLDcwMi42MjIgNTAzLjI5Niw3MTQuMzgyWiIgc3R5bGU9ImZpbGw6cmdiKDAsMTUyLDI0OCk7Ii8+CiAgICA8L2c+Cjwvc3ZnPgo=\"\n  }\n}\n"
  },
  {
    "path": "backend/mapper/authenticator_mapper.go",
    "content": "package mapper\n\nimport (\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\tkjson \"github.com/knadh/koanf/parsers/json\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n)\n\n//go:embed aaguid.json\nvar authenticatorMetadataJson []byte\n\ntype Authenticator struct {\n\tName      string `json:\"name\"`\n\tIconLight string `json:\"icon_light\"`\n\tIconDark  string `json:\"icon_dark\"`\n}\n\ntype AuthenticatorMetadata map[string]Authenticator\n\nfunc (am AuthenticatorMetadata) GetNameForAaguid(aaguid uuid.UUID) *string {\n\tif am != nil {\n\t\tif authenticatorMetadata, ok := am[aaguid.String()]; ok {\n\t\t\treturn &authenticatorMetadata.Name\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc LoadAuthenticatorMetadata(authMetaFilePath *string) AuthenticatorMetadata {\n\tk, err := config.LoadFile(authMetaFilePath, kjson.Parser())\n\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn nil\n\t}\n\n\tvar authenticatorMetadata AuthenticatorMetadata\n\n\tif k == nil {\n\t\tlog.Println(\"no authenticator metadata file provided. Using embedded one.\")\n\t\tauthenticatorMetadata, err = loadFromEmbeddedFile()\n\t\tif err != nil {\n\t\t\tlog.Println(\"no valid authenticator metadata file provided. Skipping...\")\n\t\t}\n\n\t\treturn authenticatorMetadata\n\t}\n\n\terr = k.Unmarshal(\"\", &authenticatorMetadata)\n\tif err != nil {\n\t\tlog.Println(fmt.Errorf(\"unable to unmarshal authenticator metadata: %w\", err))\n\t\treturn nil\n\t}\n\n\treturn authenticatorMetadata\n}\n\nfunc loadFromEmbeddedFile() (AuthenticatorMetadata, error) {\n\tvar authMeta AuthenticatorMetadata\n\terr := json.Unmarshal(authenticatorMetadataJson, &authMeta)\n\tif err != nil {\n\t\tlog.Println(fmt.Errorf(\"unable to unmarshal authenticator metadata: %w\", err))\n\t\treturn nil, err\n\t}\n\n\treturn authMeta, nil\n}\n"
  },
  {
    "path": "backend/middleware/logger.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/labstack/echo/v4/middleware\"\n)\n\nfunc GetLoggerMiddleware() echo.MiddlewareFunc {\n\treturn middleware.LoggerWithConfig(middleware.LoggerConfig{\n\t\tFormat: `{\"time\":\"${time_rfc3339_nano}\",\"time_unix\":\"${time_unix}\",\"id\":\"${id}\",\"remote_ip\":\"${remote_ip}\",` +\n\t\t\t`\"host\":\"${host}\",\"method\":\"${method}\",\"uri\":\"${uri}\",\"user_agent\":\"${user_agent}\",` +\n\t\t\t`\"status\":${status},\"error\":\"${error}\",\"latency\":${latency},\"latency_human\":\"${latency_human}\"` +\n\t\t\t`,\"bytes_in\":${bytes_in},\"bytes_out\":${bytes_out},\"referer\":\"${referer}\"}` + \"\\n\",\n\t})\n}\n"
  },
  {
    "path": "backend/middleware/session.go",
    "content": "package middleware\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\techojwt \"github.com/labstack/echo-jwt/v4\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/session\"\n\t\"net/http\"\n\t\"time\"\n)\n\n// Session is a convenience function to create a middleware.JWT with custom JWT verification\nfunc Session(cfg *config.Config, persister persistence.Persister, generator session.Manager) echo.MiddlewareFunc {\n\tc := echojwt.Config{\n\t\tContextKey:     \"session\",\n\t\tTokenLookup:    fmt.Sprintf(\"header:Authorization:Bearer,cookie:%s\", cfg.Session.Cookie.GetName()),\n\t\tParseTokenFunc: parseToken(cfg.Session, persister, generator),\n\t\tErrorHandler: func(c echo.Context, err error) error {\n\t\t\treturn echo.NewHTTPError(http.StatusUnauthorized).SetInternal(err)\n\t\t},\n\t}\n\treturn echojwt.WithConfig(c)\n}\n\ntype ParseTokenFunc = func(c echo.Context, auth string) (interface{}, error)\n\nfunc parseToken(cfg config.Session, persister persistence.Persister, generator session.Manager) ParseTokenFunc {\n\treturn func(c echo.Context, auth string) (interface{}, error) {\n\t\ttoken, err := generator.Verify(auth)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// check that the session id is stored in the database\n\t\tsessionId, ok := token.Get(\"session_id\")\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"no session id found in token\")\n\t\t}\n\t\tsessionID, err := uuid.FromString(sessionId.(string))\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"session id has wrong format\")\n\t\t}\n\n\t\tsessionModel, err := persister.GetSessionPersister().Get(sessionID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to get session from database: %w\", err)\n\t\t}\n\t\tif sessionModel == nil {\n\t\t\treturn nil, fmt.Errorf(\"session id not found in database\")\n\t\t}\n\n\t\t// Update lastUsed field\n\t\tsessionModel.LastUsed = time.Now().UTC()\n\t\terr = persister.GetSessionPersister().Update(*sessionModel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn token, nil\n\t}\n}\n"
  },
  {
    "path": "backend/middleware/webhook.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks\"\n)\n\nfunc WebhookMiddleware(cfg *config.Config, jwkManager jwk.Generator, persister persistence.Persister) echo.MiddlewareFunc {\n\treturn func(next echo.HandlerFunc) echo.HandlerFunc {\n\t\treturn func(ctx echo.Context) error {\n\n\t\t\tmanager, err := webhooks.NewManager(cfg, persister, jwkManager, ctx.Logger())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tctx.Set(\"webhook_manager\", manager)\n\n\t\t\treturn next(ctx)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "backend/pagination/header.go",
    "content": "package pagination\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n)\n\nfunc CreateHeader(u *url.URL, total int, page int, itemsPerPage int) string {\n\tvar lastPage int\n\n\tif total%itemsPerPage == 0 {\n\t\tlastPage = total / itemsPerPage\n\t} else {\n\t\tlastPage = total/itemsPerPage + 1\n\t}\n\n\tif lastPage == 0 {\n\t\tlastPage = 1\n\t}\n\n\tvar links []string\n\tif page != 1 || page == lastPage {\n\t\tlinks = append(links, formatter(u, \"first\", itemsPerPage, 1))\n\t}\n\n\tif page != lastPage {\n\t\tlinks = append(links, formatter(u, \"last\", itemsPerPage, lastPage))\n\t}\n\n\tif page < lastPage {\n\t\tlinks = append(links, formatter(u, \"next\", itemsPerPage, page+1))\n\t}\n\n\tif page > 1 {\n\t\tlinks = append(links, formatter(u, \"prev\", itemsPerPage, page-1))\n\t}\n\n\treturn strings.Join(links, \",\")\n}\n\nfunc formatter(u *url.URL, rel string, perPage int, page int) string {\n\tq := u.Query()\n\tq.Set(\"page\", fmt.Sprintf(\"%d\", page))\n\tq.Set(\"per_page\", fmt.Sprintf(\"%d\", perPage))\n\tu.RawQuery = q.Encode()\n\treturn fmt.Sprintf(\"<%s>; rel=\\\"%s\\\"\", u.String(), rel)\n}\n"
  },
  {
    "path": "backend/pagination/header_test.go",
    "content": "package pagination\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc TestCreateHeader_FirstPage(t *testing.T) {\n\tu, _ := url.Parse(\"http://localhost:8080\")\n\theader := CreateHeader(u, 95, 1, 10)\n\tassert.Equal(t, \"<http://localhost:8080?page=10&per_page=10>; rel=\\\"last\\\",<http://localhost:8080?page=2&per_page=10>; rel=\\\"next\\\"\", header)\n}\n\nfunc TestCreateHeader_LastPage(t *testing.T) {\n\tu, _ := url.Parse(\"http://localhost:8080\")\n\theader := CreateHeader(u, 95, 10, 10)\n\tassert.Equal(t, \"<http://localhost:8080?page=1&per_page=10>; rel=\\\"first\\\",<http://localhost:8080?page=9&per_page=10>; rel=\\\"prev\\\"\", header)\n}\n\nfunc TestCreateHeader_MiddlePage(t *testing.T) {\n\tu, _ := url.Parse(\"http://localhost:8080\")\n\theader := CreateHeader(u, 95, 4, 10)\n\tassert.Equal(t, \"<http://localhost:8080?page=1&per_page=10>; rel=\\\"first\\\",<http://localhost:8080?page=10&per_page=10>; rel=\\\"last\\\",<http://localhost:8080?page=5&per_page=10>; rel=\\\"next\\\",<http://localhost:8080?page=3&per_page=10>; rel=\\\"prev\\\"\", header)\n}\n\nfunc TestCreateHeader_TotalCountZero(t *testing.T) {\n\tu, _ := url.Parse(\"http://localhost:8080\")\n\theader := CreateHeader(u, 0, 1, 10)\n\tassert.Equal(t, \"<http://localhost:8080?page=1&per_page=10>; rel=\\\"first\\\"\", header)\n}\n\nfunc TestCreateHeader_ItemsPerPageGreaterTotalCount(t *testing.T) {\n\tu, _ := url.Parse(\"http://localhost:8080\")\n\theader := CreateHeader(u, 10, 1, 20)\n\tassert.Equal(t, \"<http://localhost:8080?page=1&per_page=20>; rel=\\\"first\\\"\", header)\n}\n\nfunc TestCreateHeader_UrlWithQuery(t *testing.T) {\n\tu, _ := url.Parse(\"http://localhost:8080?start_time=2022-09-12T12:48:48Z&end_time=2022-09-12T14:48:48Z\")\n\theader := CreateHeader(u, 95, 1, 10)\n\tassert.Equal(t, \"<http://localhost:8080?end_time=2022-09-12T14%3A48%3A48Z&page=10&per_page=10&start_time=2022-09-12T12%3A48%3A48Z>; rel=\\\"last\\\",<http://localhost:8080?end_time=2022-09-12T14%3A48%3A48Z&page=2&per_page=10&start_time=2022-09-12T12%3A48%3A48Z>; rel=\\\"next\\\"\", header)\n}\n"
  },
  {
    "path": "backend/persistence/audit_log_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype AuditLogPersister interface {\n\tCreate(auditLog models.AuditLog) error\n\tGet(id uuid.UUID) (*models.AuditLog, error)\n\tList(page int, perPage int, startTime *time.Time, endTime *time.Time, types []string, userId string, email string, ip string, searchString string) ([]models.AuditLog, error)\n\tCount(startTime *time.Time, endTime *time.Time, types []string, userId string, email string, ip string, searchString string) (int, error)\n\tCleanup[models.AuditLog]\n}\n\ntype auditLogPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewAuditLogPersister(db *pop.Connection) AuditLogPersister {\n\treturn &auditLogPersister{db: db}\n}\n\nfunc (p *auditLogPersister) Create(auditLog models.AuditLog) error {\n\tvErr, err := p.db.ValidateAndCreate(&auditLog)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store auditlog: %w\", err)\n\t}\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"auditlog object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *auditLogPersister) Get(id uuid.UUID) (*models.AuditLog, error) {\n\tauditLog := models.AuditLog{}\n\terr := p.db.Eager().Find(&auditLog, id)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get auditlog: %w\", err)\n\t}\n\n\treturn &auditLog, nil\n}\n\nfunc (p *auditLogPersister) List(page int, perPage int, startTime *time.Time, endTime *time.Time, types []string, userId string, email string, ip string, searchString string) ([]models.AuditLog, error) {\n\tauditLogs := []models.AuditLog{}\n\n\tquery := p.db.Q()\n\tquery = p.addQueryParamsToSqlQuery(query, startTime, endTime, types, userId, email, ip, searchString)\n\terr := query.Paginate(page, perPage).Order(\"created_at desc\").All(&auditLogs)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn auditLogs, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fetch auditLogs: %w\", err)\n\t}\n\n\treturn auditLogs, nil\n}\n\nfunc (p *auditLogPersister) Count(startTime *time.Time, endTime *time.Time, types []string, userId string, email string, ip string, searchString string) (int, error) {\n\tquery := p.db.Q()\n\tquery = p.addQueryParamsToSqlQuery(query, startTime, endTime, types, userId, email, ip, searchString)\n\tcount, err := query.Count(&models.AuditLog{})\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to get auditLog count: %w\", err)\n\t}\n\n\treturn count, nil\n}\n\nfunc (p *auditLogPersister) addQueryParamsToSqlQuery(query *pop.Query, startTime *time.Time, endTime *time.Time, types []string, userId string, email string, ip string, searchString string) *pop.Query {\n\tif startTime != nil {\n\t\tquery = query.Where(\"created_at > ?\", startTime)\n\t}\n\tif endTime != nil {\n\t\tquery = query.Where(\"created_at < ?\", endTime)\n\t}\n\n\tif len(types) > 0 {\n\t\tquery = query.Where(\"type IN (?)\", types)\n\t}\n\n\tif len(userId) > 0 {\n\t\tswitch p.db.Dialect.Name() {\n\t\tcase \"postgres\", \"cockroach\":\n\t\t\tquery = query.Where(\"actor_user_id::text LIKE ?\", \"%\"+userId+\"%\")\n\t\tcase \"mysql\", \"mariadb\":\n\t\t\tquery = query.Where(\"actor_user_id LIKE ?\", \"%\"+userId+\"%\")\n\t\t}\n\t}\n\n\tif len(email) > 0 {\n\t\tquery = query.Where(\"actor_email LIKE ?\", \"%\"+email+\"%\")\n\t}\n\n\tif len(ip) > 0 {\n\t\tquery = query.Where(\"meta_source_ip LIKE ?\", \"%\"+ip+\"%\")\n\t}\n\n\tif len(searchString) > 0 {\n\t\tswitch p.db.Dialect.Name() {\n\t\tcase \"postgres\", \"cockroach\":\n\t\t\targ := \"%\" + searchString + \"%\"\n\t\t\tquery = query.Where(\"(actor_email LIKE ? OR meta_source_ip LIKE ? OR actor_user_id::text LIKE ?)\", arg, arg, arg)\n\t\tcase \"mysql\", \"mariadb\":\n\t\t\targ := \"%\" + searchString + \"%\"\n\t\t\tquery = query.Where(\"(actor_email LIKE ? OR meta_source_ip LIKE ? OR actor_user_id LIKE ?)\", arg, arg, arg)\n\t\t}\n\t}\n\n\treturn query\n}\n\nfunc (p *auditLogPersister) FindExpired(cutoffTime time.Time, page, perPage int) ([]models.AuditLog, error) {\n\tvar items []models.AuditLog\n\n\tquery := p.db.\n\t\tWhere(\"created_at < ?\", cutoffTime).\n\t\tSelect(\"id\").\n\t\tPaginate(page, perPage)\n\terr := query.All(&items)\n\n\treturn items, err\n}\n\nfunc (p *auditLogPersister) Delete(auditLog models.AuditLog) error {\n\terr := p.db.Eager().Destroy(&auditLog)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete auditlog: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/email_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype EmailPersister interface {\n\tGet(emailId uuid.UUID) (*models.Email, error)\n\tCountByUserId(uuid.UUID) (int, error)\n\tFindByUserId(uuid.UUID) (models.Emails, error)\n\tFindByAddress(string) (*models.Email, error)\n\tCreate(models.Email) error\n\tUpdate(models.Email) error\n\tDelete(models.Email) error\n}\n\ntype emailPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewEmailPersister(db *pop.Connection) EmailPersister {\n\treturn &emailPersister{db: db}\n}\n\nfunc (e *emailPersister) Get(emailId uuid.UUID) (*models.Email, error) {\n\temail := models.Email{}\n\terr := e.db.Find(&email, emailId.String())\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &email, nil\n}\n\nfunc (e *emailPersister) FindByUserId(userId uuid.UUID) (models.Emails, error) {\n\tvar emails models.Emails\n\n\terr := e.db.EagerPreload().\n\t\tWhere(\"user_id = ?\", userId.String()).\n\t\tOrder(\"created_at asc\").\n\t\tAll(&emails)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn emails, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn emails, nil\n}\n\nfunc (e *emailPersister) CountByUserId(userId uuid.UUID) (int, error) {\n\tvar emails []models.Email\n\n\tcount, err := e.db.\n\t\tWhere(\"user_id = ?\", userId.String()).\n\t\tCount(&emails)\n\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn count, nil\n}\n\nfunc (e *emailPersister) FindByAddress(address string) (*models.Email, error) {\n\tvar email models.Email\n\n\tquery := e.db.EagerPreload().Where(\"address = ?\", address)\n\terr := query.First(&email)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &email, nil\n}\n\nfunc (e *emailPersister) Create(email models.Email) error {\n\tvErr, err := e.db.ValidateAndCreate(&email)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"email object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (e *emailPersister) Update(email models.Email) error {\n\tvErr, err := e.db.ValidateAndUpdate(&email)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"email object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (e *emailPersister) Delete(email models.Email) error {\n\terr := e.db.Destroy(&email)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete email: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/flow_persister.go",
    "content": "package persistence\n\nimport (\n\t\"errors\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\ntype FlowPersister interface {\n\tflowpilot.FlowDB\n\tCleanup[models.Flow]\n}\n\ntype flowPersister struct {\n\ttx *pop.Connection\n}\n\nfunc NewFlowPersister(tx *pop.Connection) FlowPersister {\n\treturn flowPersister{tx}\n}\n\nfunc (p flowPersister) GetFlow(flowID uuid.UUID) (*flowpilot.FlowModel, error) {\n\tflowModel := models.Flow{}\n\n\terr := p.tx.Find(&flowModel, flowID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn flowModel.ToFlowpilotModel(), nil\n}\n\nfunc (p flowPersister) CreateFlow(flowModel flowpilot.FlowModel) error {\n\tf := models.Flow{\n\t\tID:        flowModel.ID,\n\t\tData:      flowModel.Data,\n\t\tVersion:   flowModel.Version,\n\t\tCSRFToken: flowModel.CSRFToken,\n\t\tExpiresAt: flowModel.ExpiresAt,\n\t\tCreatedAt: flowModel.CreatedAt,\n\t\tUpdatedAt: flowModel.UpdatedAt,\n\t}\n\n\terr := p.tx.Create(&f)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (p flowPersister) UpdateFlow(flowModel flowpilot.FlowModel) error {\n\tf := &models.Flow{\n\t\tID:        flowModel.ID,\n\t\tData:      flowModel.Data,\n\t\tVersion:   flowModel.Version,\n\t\tCSRFToken: flowModel.CSRFToken,\n\t\tExpiresAt: flowModel.ExpiresAt,\n\t\tCreatedAt: flowModel.CreatedAt,\n\t\tUpdatedAt: flowModel.UpdatedAt,\n\t}\n\n\tpreviousVersion := flowModel.Version - 1\n\n\tcount, err := p.tx.\n\t\tWhere(\"id = ?\", f.ID).\n\t\tWhere(\"version = ?\", previousVersion).\n\t\tUpdateQuery(f, \"version\", \"csrf_token\", \"data\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif count != 1 {\n\t\treturn errors.New(\"version conflict while updating the flow\")\n\t}\n\n\treturn nil\n}\n\nfunc (p flowPersister) FindExpired(cutoffTime time.Time, page, perPage int) ([]models.Flow, error) {\n\tvar items []models.Flow\n\n\tquery := p.tx.\n\t\tWhere(\"expires_at < ?\", cutoffTime).\n\t\tSelect(\"id\").\n\t\tPaginate(page, perPage)\n\terr := query.All(&items)\n\n\treturn items, err\n}\n\nfunc (p flowPersister) Delete(item models.Flow) error {\n\treturn p.tx.Destroy(&item)\n}\n"
  },
  {
    "path": "backend/persistence/identity_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype IdentityPersister interface {\n\tGet(providerUserID string, providerID string) (*models.Identity, error)\n\tGetByID(identityID uuid.UUID) (*models.Identity, error)\n\tCreate(identity models.Identity) error\n\tUpdate(identity models.Identity) error\n\tDelete(identity models.Identity) error\n}\n\ntype identityPersister struct {\n\tdb *pop.Connection\n}\n\nfunc (p identityPersister) GetByID(identityID uuid.UUID) (*models.Identity, error) {\n\tidentity := &models.Identity{}\n\tif err := p.db.EagerPreload(\"Email\", \"Email.User\", \"Email.User.Username\", \"SamlIdentity\").Find(identity, identityID); err != nil {\n\t\tif errors.Is(err, sql.ErrNoRows) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to get identity: %w\", err)\n\t}\n\treturn identity, nil\n}\n\nfunc (p identityPersister) Get(providerUserID string, providerID string) (*models.Identity, error) {\n\tidentity := &models.Identity{}\n\tif err := p.db.EagerPreload().Where(\"provider_user_id = ? AND provider_id = ?\", providerUserID, providerID).First(identity); err != nil {\n\t\tif errors.Is(err, sql.ErrNoRows) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to get identity: %w\", err)\n\t}\n\treturn identity, nil\n}\n\nfunc (p identityPersister) Create(identity models.Identity) error {\n\tvErr, err := p.db.ValidateAndCreate(&identity)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store identity: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"identity object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p identityPersister) Update(identity models.Identity) error {\n\tvErr, err := p.db.ValidateAndUpdate(&identity)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update identity: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"identity object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p identityPersister) Delete(identity models.Identity) error {\n\terr := p.db.Destroy(&identity)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete identity: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc NewIdentityPersister(db *pop.Connection) IdentityPersister {\n\treturn &identityPersister{db: db}\n}\n"
  },
  {
    "path": "backend/persistence/jwk_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype JwkPersister interface {\n\tGet(int) (*models.Jwk, error)\n\tGetAll() ([]models.Jwk, error)\n\tGetLast() (*models.Jwk, error)\n\tCreate(models.Jwk) error\n}\n\ntype jwkPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewJwkPersister(db *pop.Connection) JwkPersister {\n\treturn &jwkPersister{db: db}\n}\n\nfunc (p *jwkPersister) Get(id int) (*models.Jwk, error) {\n\tjwk := models.Jwk{}\n\terr := p.db.Find(&jwk, id)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get jwk: %w\", err)\n\t}\n\treturn &jwk, nil\n}\n\nfunc (p *jwkPersister) GetAll() ([]models.Jwk, error) {\n\tjwks := []models.Jwk{}\n\terr := p.db.All(&jwks)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get all jwks: %w\", err)\n\t}\n\treturn jwks, nil\n}\n\nfunc (p *jwkPersister) GetLast() (*models.Jwk, error) {\n\tjwk := models.Jwk{}\n\terr := p.db.Last(&jwk)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get jwk: %w\", err)\n\t}\n\treturn &jwk, nil\n}\n\nfunc (p *jwkPersister) Create(jwk models.Jwk) error {\n\tvErr, err := p.db.ValidateAndCreate(&jwk)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store jwk: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"jwk object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220405153240_create_users.down.fizz",
    "content": "drop_table(\"users\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220405153240_create_users.up.fizz",
    "content": "create_table(\"users\") {\n    t.Column(\"id\", \"uuid\", {primary: true})\n    t.Column(\"email\", \"string\", {})\n    t.Column(\"verified\", \"bool\", {})\n    t.Timestamps()\n    t.Index(\"email\", {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220405153750_create_passcodes.down.fizz",
    "content": "drop_table(\"passcodes\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220405153750_create_passcodes.up.fizz",
    "content": "create_table(\"passcodes\") {\n    t.Column(\"id\", \"uuid\", {primary: true})\n    t.Column(\"user_id\", \"uuid\", {})\n    t.Column(\"ttl\", \"integer\", {})\n    t.Column(\"code\", \"string\", {})\n    t.Column(\"try_count\", \"integer\", {})\n    t.Timestamps()\n    t.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220405154240_create_webauthn_credentials.down.fizz",
    "content": "drop_table(\"webauthn_credentials\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220405154240_create_webauthn_credentials.up.fizz",
    "content": "create_table(\"webauthn_credentials\") {\n    t.Column(\"id\", \"string\", {primary: true})\n    t.Column(\"user_id\", \"uuid\", {})\n    t.Column(\"public_key\", \"text\", {})\n    t.Column(\"attestation_type\", \"string\", {})\n    t.Column(\"aaguid\", \"uuid\", {})\n    t.Column(\"sign_count\", \"integer\", {})\n    t.Timestamps()\n    t.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220405154750_create_webauthn_session_data.down.fizz",
    "content": "drop_table(\"webauthn_session_data\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220405154750_create_webauthn_session_data.up.fizz",
    "content": "create_table(\"webauthn_session_data\") {\n    t.Column(\"id\", \"uuid\", {primary: true})\n    t.Column(\"challenge\", \"string\", {})\n    t.Column(\"user_id\", \"uuid\", {})\n    t.Column(\"user_verification\", \"string\", {})\n    t.Column(\"operation\", \"string\", {})\n    t.Timestamps()\n    t.Index(\"challenge\", {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220405155120_create_webauthn_session_data_allowed_credentials.down.fizz",
    "content": "drop_table(\"webauthn_session_data_allowed_credentials\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220405155120_create_webauthn_session_data_allowed_credentials.up.fizz",
    "content": "create_table(\"webauthn_session_data_allowed_credentials\") {\n    t.Column(\"id\", \"uuid\", {primary: true})\n    t.Column(\"credential_id\", \"string\", {})\n    t.Column(\"webauthn_session_data_id\", \"uuid\", {})\n    t.Timestamps()\n    t.ForeignKey(\"webauthn_session_data_id\", {\"webauthn_session_data\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220413152500_create_jwk.down.fizz",
    "content": "drop_table(\"jwks\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220413152500_create_jwk.up.fizz",
    "content": "create_table(\"jwks\") {\n    t.Column(\"id\", \"int\", {primary: true})\n    t.Column(\"key_data\", \"text\", {})\n    t.Column(\"created_at\", \"timestamp\", {})\n    t.DisableTimestamps()\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220425122015_create_password_credentials.down.fizz",
    "content": "drop_table(\"password_credentials\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220425122015_create_password_credentials.up.fizz",
    "content": "create_table(\"password_credentials\") {\n    t.Column(\"id\", \"uuid\", {primary: true})\n    t.Column(\"user_id\", \"uuid\", {})\n    t.Column(\"password\", \"string\", {})\n    t.Timestamps()\n    t.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n    t.Index(\"user_id\", {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220711121022_create_credential_transports.down.fizz",
    "content": "drop_table(\"webauthn_credential_transports\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220711121022_create_credential_transports.up.fizz",
    "content": "create_table(\"webauthn_credential_transports\") {\n    t.Column(\"id\", \"string\", {\"primary\": true})\n    t.Column(\"name\", \"string\", {})\n    t.Column(\"webauthn_credential_id\", \"string\", {})\n    t.DisableTimestamps()\n    t.ForeignKey(\"webauthn_credential_id\", {\"webauthn_credentials\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n    t.Index([\"name\", \"webauthn_credential_id\"], {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20220818111000_create_audit_logs.down.fizz",
    "content": "drop_table(\"audit_logs\")\n"
  },
  {
    "path": "backend/persistence/migrations/20220818111000_create_audit_logs.up.fizz",
    "content": "create_table(\"audit_logs\") {\n    t.Column(\"id\", \"uuid\", {\"primary\": true})\n    t.Column(\"type\", \"string\", {})\n    t.Column(\"error\", \"string\", {\"null\": true})\n    t.Column(\"meta_http_request_id\", \"string\", {})\n    t.Column(\"meta_source_ip\", \"string\", {})\n    t.Column(\"meta_user_agent\", \"string\", {})\n    t.Column(\"actor_user_id\", \"uuid\", {\"null\": true})\n    t.Column(\"actor_email\", \"string\", {\"null\": true})\n    t.Timestamps()\n    t.Index(\"type\")\n    t.Index(\"actor_user_id\")\n    t.Index(\"actor_email\")\n    t.Index(\"meta_http_request_id\")\n    t.Index(\"meta_source_ip\")\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20221027104800_create_emails.down.fizz",
    "content": "drop_table(\"primary_emails\")\ndrop_table(\"emails\")\n"
  },
  {
    "path": "backend/persistence/migrations/20221027104800_create_emails.up.fizz",
    "content": "create_table(\"emails\") {\n\tt.Column(\"id\", \"uuid\")\n\tt.Column(\"user_id\", \"uuid\", { \"null\": true })\n\tt.Column(\"address\", \"string\")\n\tt.Column(\"verified\", \"bool\")\n\tt.PrimaryKey(\"id\")\n\tt.Index(\"address\", { \"unique\": true })\n\tt.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n\ncreate_table(\"primary_emails\") {\n    t.Column(\"id\", \"uuid\")\n    t.Column(\"email_id\", \"uuid\")\n    t.Column(\"user_id\", \"uuid\")\n    t.PrimaryKey(\"id\")\n    t.Index(\"email_id\", { \"unique\": true })\n    t.Index(\"user_id\", { \"unique\": true })\n    t.ForeignKey(\"email_id\", {\"emails\": [\"id\"]}, {\"on_delete\": \"restrict\", \"on_update\": \"cascade\"})\n    t.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n\nsql(\"INSERT INTO emails (id, user_id, address, verified, created_at, updated_at)\nSELECT id, id, email, verified, created_at, updated_at\nFROM users\")\n\nsql(\"INSERT INTO primary_emails (id, email_id, user_id, created_at, updated_at)\nSELECT id, id, user_id, created_at, updated_at FROM emails\")\n"
  },
  {
    "path": "backend/persistence/migrations/20221027104900_change_users.down.fizz",
    "content": "add_column(\"users\", \"email\", \"string\", { \"null\": true })\nadd_column(\"users\", \"verified\", \"bool\", { \"null\": true })\n\nsql(\"\nUPDATE users u\nSET email = (\n  SELECT e.address\n  FROM emails e\n  JOIN primary_emails pe\n    ON e.id = pe.email_id AND e.user_id = u.id\n  LIMIT 1\n),\n  verified = (\n    SELECT e.verified\n    FROM emails e\n    JOIN primary_emails pe\n      ON e.id = pe.email_id AND e.user_id = u.id\n    LIMIT 1\n)\")\n\nchange_column(\"users\", \"email\", \"string\", { \"null\": false, \"unique\": true })\nchange_column(\"users\", \"verified\", \"bool\", { \"null\": false })\n"
  },
  {
    "path": "backend/persistence/migrations/20221027104900_change_users.up.fizz",
    "content": "drop_column(\"users\", \"email\")\ndrop_column(\"users\", \"verified\")\n\n"
  },
  {
    "path": "backend/persistence/migrations/20221027123530_change_passcodes.down.fizz",
    "content": "drop_foreign_key(\"passcodes\", \"passcodes_emails_id_fk\", {\"if_exists\": false})\ndrop_column(\"passcodes\", \"email_id\")\n"
  },
  {
    "path": "backend/persistence/migrations/20221027123530_change_passcodes.up.fizz",
    "content": "add_column(\"passcodes\", \"email_id\", \"uuid\", { \"null\": true })\nadd_foreign_key(\"passcodes\", \"email_id\", {\"emails\": [\"id\"]}, {\n    \"on_delete\": \"cascade\",\n    \"on_update\": \"cascade\",\n})\n"
  },
  {
    "path": "backend/persistence/migrations/20221222134900_change_webauthn_credentials.down.fizz",
    "content": "drop_column(\"webauthn_credentials\", \"name\")\n"
  },
  {
    "path": "backend/persistence/migrations/20221222134900_change_webauthn_credentials.up.fizz",
    "content": "add_column(\"webauthn_credentials\", \"name\", \"string\", { \"null\": true })\n"
  },
  {
    "path": "backend/persistence/migrations/20230112152816_create_identities.down.fizz",
    "content": "drop_table(\"identities\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230112152816_create_identities.up.fizz",
    "content": "create_table(\"identities\") {\n    t.Column(\"id\", \"uuid\", {})\n    t.Column(\"provider_id\", \"string\", {})\n    t.Column(\"provider_name\", \"string\", {})\n    t.Column(\"data\", \"text\", {\"null\": true})\n    t.Column(\"email_id\", \"uuid\", {})\n    t.Timestamps()\n    t.PrimaryKey(\"id\")\n    t.ForeignKey(\"email_id\", {\"emails\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n    t.Index([\"provider_id\", \"provider_name\"], {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20230206102000_change_webauthn_credentials.down.fizz",
    "content": "drop_column(\"webauthn_credentials\", \"backup_eligible\")\ndrop_column(\"webauthn_credentials\", \"backup_state\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230206102000_change_webauthn_credentials.up.fizz",
    "content": "add_column(\"webauthn_credentials\", \"backup_eligible\", \"bool\", {\"default\": false})\nadd_column(\"webauthn_credentials\", \"backup_state\", \"bool\", {\"default\": false})\n"
  },
  {
    "path": "backend/persistence/migrations/20230222114100_change_webauthn_credentials.down.fizz",
    "content": "drop_column(\"webauthn_credentials\", \"last_used_at\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230222114100_change_webauthn_credentials.up.fizz",
    "content": "add_column(\"webauthn_credentials\", \"last_used_at\", \"timestamp\", { \"null\": true })\n"
  },
  {
    "path": "backend/persistence/migrations/20230317114334_create_tokens.down.fizz",
    "content": "drop_table(\"tokens\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230317114334_create_tokens.up.fizz",
    "content": "create_table(\"tokens\") {\n    t.Column(\"id\", \"uuid\", {})\n    t.Column(\"user_id\", \"uuid\", {})\n    t.Column(\"value\", \"string\", {})\n    t.Column(\"expires_at\", \"timestamp\", {})\n    t.Timestamps()\n    t.PrimaryKey(\"id\")\n    t.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n    t.Index(\"value\", {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20230801124808_webauthn_session_data_add_expiry.down.fizz",
    "content": "drop_column(\"webauthn_session_data\", \"expires_at\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230801124808_webauthn_session_data_add_expiry.up.fizz",
    "content": "add_column(\"webauthn_session_data\", \"expires_at\",  \"timestamp\", {\"null\": true})\n"
  },
  {
    "path": "backend/persistence/migrations/20230810173315_create_flows.down.fizz",
    "content": "drop_table(\"transitions\")\ndrop_table(\"flows\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230810173315_create_flows.up.fizz",
    "content": "create_table(\"flows\") {\n  t.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"current_state\", \"string\")\n  t.Column(\"stash_data\", \"string\", {\"size\": 4096})\n  t.Column(\"version\", \"int\")\n  t.Column(\"expires_at\", \"timestamp\")\n  t.Timestamps()\n}\n\ncreate_table(\"transitions\") {\n  t.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"flow_id\", \"uuid\")\n  t.Column(\"action\", \"string\")\n  t.Column(\"from_state\", \"string\")\n  t.Column(\"to_state\", \"string\")\n  t.Column(\"input_data\", \"string\")\n  t.Column(\"error_code\", \"string\", {\"null\": true})\n  t.ForeignKey(\"flow_id\", {\"flows\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n  t.Timestamps()\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20230905102601_create_saml_state.down.fizz",
    "content": "drop_table(\"saml_states\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230905102601_create_saml_state.up.fizz",
    "content": "create_table(\"saml_states\") {\n    t.Column(\"id\", \"uuid\", {})\n    t.Column(\"nonce\", \"string\", {})\n    t.Column(\"state\", \"varchar(500)\", {})\n    t.Column(\"expires_at\", \"timestamp\", {})\n    t.Timestamps()\n    t.PrimaryKey(\"id\")\n    t.Index(\"nonce\", {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20230915111552_create_saml_certs.down.fizz",
    "content": "drop_table(\"saml_certificates\")\n"
  },
  {
    "path": "backend/persistence/migrations/20230915111552_create_saml_certs.up.fizz",
    "content": "create_table(\"saml_certificates\") {\n    t.Column(\"id\", \"uuid\", {})\n    t.Column(\"cert_data\", \"text\", {})\n    t.Column(\"cert_key\", \"text\", {})\n    t.Column(\"encryption_key\", \"string\", {})\n    t.Timestamps()\n\n    t.PrimaryKey(\"id\")\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20231012141100_change_user_table.down.fizz",
    "content": "drop_column(\"users\", \"username\")\n"
  },
  {
    "path": "backend/persistence/migrations/20231012141100_change_user_table.up.fizz",
    "content": "add_column(\"users\", \"username\", \"string\", { \"null\": true })\n"
  },
  {
    "path": "backend/persistence/migrations/20231013113800_change_passcode_table.down.fizz",
    "content": "sql(\"DELETE FROM passcodes WHERE user_id IS NULL\")\nchange_column(\"passcodes\", \"user_id\", \"uuid\", {})\n\ndrop_foreign_key(\"passcodes\", \"passcodes_flows_id_fk\", {\"if_exists\": false})\ndrop_column(\"passcodes\", \"flow_id\")\n"
  },
  {
    "path": "backend/persistence/migrations/20231013113800_change_passcode_table.up.fizz",
    "content": "change_column(\"passcodes\", \"user_id\", \"uuid\", {\"null\": true})\n\nadd_column(\"passcodes\", \"flow_id\", \"uuid\", {\"null\":true})\nadd_foreign_key(\"passcodes\", \"flow_id\", {\"flows\": [\"id\"]}, {\n    \"on_delete\": \"cascade\",\n    \"on_update\": \"cascade\",\n})\n"
  },
  {
    "path": "backend/persistence/migrations/20240108094151_create_webhooks.down.fizz",
    "content": "drop_table(\"webhooks\")"
  },
  {
    "path": "backend/persistence/migrations/20240108094151_create_webhooks.up.fizz",
    "content": "create_table(\"webhooks\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"callback\", \"string\", {})\n    t.Column(\"enabled\", \"bool\", { \"default\": true })\n    t.Column(\"failures\", \"int\", { default: 0 })\n    t.Column(\"expires_at\", \"timestamp\", {})\n\n\tt.Timestamps()\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20240108094210_create_webhook_events.down.fizz",
    "content": "drop_table(\"webhook_events\")"
  },
  {
    "path": "backend/persistence/migrations/20240108094210_create_webhook_events.up.fizz",
    "content": "create_table(\"webhook_events\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"webhook_id\", \"uuid\", { \"null\": false })\n\tt.Column(\"event\", \"string\", { \"null\": false })\n\n\tt.Timestamps()\n\n    t.Index([\"webhook_id\", \"event\"], { \"unique\": true })\n\tt.ForeignKey(\"webhook_id\", {\"webhooks\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20240207150616_change_audit_logs.down.fizz",
    "content": "drop_column(\"audit_logs\", \"details\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240207150616_change_audit_logs.up.fizz",
    "content": "add_column(\"audit_logs\", \"details\", \"text\", {\"null\":true})\n"
  },
  {
    "path": "backend/persistence/migrations/20240530122100_change_tokens.down.fizz",
    "content": "drop_foreign_key(\"tokens\", \"tokens_identities_id_fk\")\ndrop_column(\"tokens\", \"identity_id\")\ndrop_column(\"tokens\", \"is_flow\")\ndrop_column(\"tokens\", \"user_created\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240530122100_change_tokens.up.fizz",
    "content": "add_column(\"tokens\", \"identity_id\", \"uuid\", {\"null\":true})\nadd_column(\"tokens\", \"is_flow\", \"bool\", {\"default\":false})\nadd_column(\"tokens\", \"user_created\", \"bool\", {\"default\":false})\n\nadd_foreign_key(\"tokens\", \"identity_id\", {\"identities\": [\"id\"]}, {\n    \"on_delete\": \"cascade\",\n    \"on_update\": \"cascade\",\n})\n"
  },
  {
    "path": "backend/persistence/migrations/20240530145724_change_users.down.fizz",
    "content": "drop_index(\"users\", \"users_username_idx\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240530145724_change_users.up.fizz",
    "content": "add_index(\"users\", \"username\", {\"unique\": true})\n"
  },
  {
    "path": "backend/persistence/migrations/20240612122326_change_flows.down.fizz",
    "content": "drop_column(\"flows\", \"csrf_token\")\ndrop_column(\"flows\", \"previous_state\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240612122326_change_flows.up.fizz",
    "content": "add_column(\"flows\", \"csrf_token\", \"string\", { \"size\": 32 })\nadd_column(\"flows\", \"previous_state\", \"string\", { \"null\": true })\n"
  },
  {
    "path": "backend/persistence/migrations/20240717020131_drop_transitions.down.fizz",
    "content": "create_table(\"transitions\") {\n  t.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"flow_id\", \"uuid\")\n  t.Column(\"action\", \"string\")\n  t.Column(\"from_state\", \"string\")\n  t.Column(\"to_state\", \"string\")\n  t.Column(\"input_data\", \"string\")\n  t.Column(\"error_code\", \"string\", {\"null\": true})\n  t.ForeignKey(\"flow_id\", {\"flows\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n  t.Timestamps()\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20240717020131_drop_transitions.up.fizz",
    "content": "drop_table(\"transitions\")\n\n"
  },
  {
    "path": "backend/persistence/migrations/20240717020707_change_flows.down.fizz",
    "content": "drop_column(\"flows\", \"data\")\nadd_column(\"flows\", \"stash_data\", \"string\", {\"size\": 4096})\nadd_column(\"flows\", \"current_state\", \"string\")\nadd_column(\"flows\", \"previous_state\", \"string\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240717020707_change_flows.up.fizz",
    "content": "add_column(\"flows\", \"data\", \"text\")\ndrop_column(\"flows\", \"stash_data\")\ndrop_column(\"flows\", \"current_state\")\ndrop_column(\"flows\", \"previous_state\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240723171257_change_passcodes.down.fizz",
    "content": "add_column(\"passcodes\", \"flow_id\", \"uuid\", {\"null\":true})\nadd_foreign_key(\"passcodes\", \"flow_id\", {\"flows\": [\"id\"]}, {\n    \"on_delete\": \"cascade\",\n    \"on_update\": \"cascade\",\n})\n"
  },
  {
    "path": "backend/persistence/migrations/20240723171257_change_passcodes.up.fizz",
    "content": "drop_foreign_key(\"passcodes\", \"passcodes_flows_id_fk\")\ndrop_column(\"passcodes\", \"flow_id\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240723173648_create_usernames.down.fizz",
    "content": "add_column(\"users\", \"username\", \"string\", { \"null\": true })\nadd_index(\"users\", \"username\", {\"unique\": true})\ndrop_table(\"usernames\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240723173648_create_usernames.up.fizz",
    "content": "drop_column(\"users\", \"username\")\ncreate_table(\"usernames\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"user_id\", \"uuid\", { \"null\": false })\n\tt.Column(\"username\", \"string\", { \"null\": false })\n\tt.Timestamps()\n    t.Index(\"username\", { \"unique\": true })\n    t.Index(\"user_id\", { \"unique\": true })\n\tt.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20240826132046_create_otp_secrets.down.fizz",
    "content": "drop_table(\"otp_secrets\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240826132046_create_otp_secrets.up.fizz",
    "content": "create_table(\"otp_secrets\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"user_id\", \"uuid\", {\"null\": false})\n\tt.Column(\"secret\", \"string\", {\"null\": false})\n\tt.Timestamps()\n\tt.Index(\"user_id\", {\"unique\": true})\n\tt.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20240826133417_change_webauthn_credentials.down.fizz",
    "content": "drop_column(\"webauthn_credentials\", \"mfa_only\")\n"
  },
  {
    "path": "backend/persistence/migrations/20240826133417_change_webauthn_credentials.up.fizz",
    "content": "add_column(\"webauthn_credentials\", \"mfa_only\", \"bool\", {\"default\": false} )\n"
  },
  {
    "path": "backend/persistence/migrations/20241002113000_create_sessions.down.fizz",
    "content": "drop_table(\"sessions\")\n"
  },
  {
    "path": "backend/persistence/migrations/20241002113000_create_sessions.up.fizz",
    "content": "create_table(\"sessions\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"user_id\", \"uuid\", { \"null\": false })\n\tt.Column(\"user_agent\", \"string\", { \"null\": false })\n\tt.Column(\"ip_address\", \"string\", { \"null\": false })\n\tt.Column(\"expires_at\", \"timestamp\", { \"null\": true })\n\tt.Column(\"last_used\", \"timestamp\", { \"null\": false })\n\tt.Timestamps()\n\tt.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20241106171500_change_sessions.down.fizz",
    "content": "change_column(\"sessions\", \"user_agent\", \"string\", {\"null\": false})\nchange_column(\"sessions\", \"ip_address\", \"string\", {\"null\": false})\n"
  },
  {
    "path": "backend/persistence/migrations/20241106171500_change_sessions.up.fizz",
    "content": "change_column(\"sessions\", \"user_agent\", \"string\", {\"null\": true})\nchange_column(\"sessions\", \"ip_address\", \"string\", {\"null\": true})\n"
  },
  {
    "path": "backend/persistence/migrations/20241112181011_create_trusted_devices.down.fizz",
    "content": "drop_table(\"trusted_devices\")\n"
  },
  {
    "path": "backend/persistence/migrations/20241112181011_create_trusted_devices.up.fizz",
    "content": "create_table(\"trusted_devices\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"user_id\", \"uuid\", { \"null\": false })\n\tt.Column(\"device_token\", \"string\", { \"null\": false, \"size\": 128 })\n\tt.Column(\"expires_at\", \"timestamp\", {})\n\tt.Timestamps()\n\tt.Index(\"device_token\")\n\tt.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20241118114500_change_webauthn_credentials.down.fizz",
    "content": "drop_foreign_key(\"webauthn_credentials\", \"webauthn_credential_user_handle_fkey\")\ndrop_foreign_key(\"webauthn_credentials\", \"webauthn_credentials_webauthn_credential_user_handles_id_fk\")\ndrop_column(\"webauthn_credentials\", \"user_handle_id\")\ndrop_table(\"webauthn_credential_user_handles\")\n\n\n"
  },
  {
    "path": "backend/persistence/migrations/20241118114500_change_webauthn_credentials.up.fizz",
    "content": "create_table(\"webauthn_credential_user_handles\") {\n    t.Column(\"id\", \"uuid\", {primary: true})\n    t.Column(\"user_id\", \"uuid\", {\"null\": false})\n    t.Column(\"handle\", \"string\", {\"null\": false})\n    t.Timestamps()\n    t.Index(\"handle\", {\"unique\": true})\n    t.Index([\"id\", \"user_id\"], {\"unique\": true})\n    t.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n}\n\nadd_column(\"webauthn_credentials\", \"user_handle_id\", \"uuid\", { \"null\": true })\nadd_foreign_key(\"webauthn_credentials\", \"user_handle_id\", {\"webauthn_credential_user_handles\": [\"id\"]}, {\n    \"on_delete\": \"set null\",\n    \"on_update\": \"cascade\",\n})\n\nsql(\"ALTER TABLE webauthn_credentials ADD CONSTRAINT webauthn_credential_user_handle_fkey FOREIGN KEY (user_handle_id, user_id) REFERENCES webauthn_credential_user_handles(id, user_id) ON DELETE NO ACTION ON UPDATE CASCADE;\")\n"
  },
  {
    "path": "backend/persistence/migrations/20250130154010_change_identities.down.fizz",
    "content": "drop_index(\"identities\", \"identities_provider_user_id_provider_id_idx\")\n\nrename_column(\"identities\", \"provider_id\", \"provider_name\")\nrename_column(\"identities\", \"provider_user_id\", \"provider_id\")\n\nadd_index(\"identities\", [\"provider_id\", \"provider_name\"], {unique: true})\n"
  },
  {
    "path": "backend/persistence/migrations/20250130154010_change_identities.up.fizz",
    "content": "drop_index(\"identities\", \"identities_provider_id_provider_name_idx\")\n\nrename_column(\"identities\", \"provider_id\", \"provider_user_id\")\nrename_column(\"identities\", \"provider_name\", \"provider_id\")\n\nadd_index(\"identities\", [\"provider_user_id\", \"provider_id\"], {unique: true})\n"
  },
  {
    "path": "backend/persistence/migrations/20250130170131_create_saml_identities.down.fizz",
    "content": "drop_table(\"saml_identities\")\n"
  },
  {
    "path": "backend/persistence/migrations/20250130170131_create_saml_identities.up.fizz",
    "content": "create_table(\"saml_identities\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"identity_id\", \"uuid\", { \"null\": false })\n\tt.Column(\"domain\", \"string\", { \"null\": false })\n\tt.Timestamps()\n\tt.ForeignKey(\"identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\", \"on_update\": \"cascade\"})\n\tt.Index([\"identity_id\", \"domain\"], {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20250210095906_create_saml_idp_initiated_requests.down.fizz",
    "content": "drop_table(\"saml_idp_initiated_requests\")\n"
  },
  {
    "path": "backend/persistence/migrations/20250210095906_create_saml_idp_initiated_requests.up.fizz",
    "content": "create_table(\"saml_idp_initiated_requests\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"response_id\", \"string\", { \"null\": false })\n\tt.Column(\"issuer\", \"string\", { \"null\": false })\n\tt.Column(\"expires_at\", \"timestamp\", { \"null\": false })\n    t.Column(\"created_at\", \"timestamp\", { \"null\": false })\n    t.DisableTimestamps()\n    t.Index([\"response_id\", \"issuer\"], {\"unique\": true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20250313160348_change_flows.down.fizz",
    "content": ""
  },
  {
    "path": "backend/persistence/migrations/20250313160348_change_flows.up.fizz",
    "content": "change_column(\"flows\", \"data\", \"text\")\n"
  },
  {
    "path": "backend/persistence/migrations/20250414165334_create_user_metadata.down.fizz",
    "content": "drop_table(\"user_metadata\")\n"
  },
  {
    "path": "backend/persistence/migrations/20250414165334_create_user_metadata.up.fizz",
    "content": "create_table(\"user_metadata\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\tt.Column(\"user_id\", \"uuid\", {})\n\tt.Column(\"public_metadata\", \"string\", {\"null\": true, size: 3000})\n\tt.Column(\"private_metadata\", \"string\", {\"null\": true, size: 3000})\n\tt.Column(\"unsafe_metadata\", \"string\", {\"null\": true, size: 3000})\n\tt.Column(\"created_at\", \"timestamp\", {})\n\tt.Column(\"updated_at\", \"timestamp\", {})\n\n\tt.ForeignKey(\"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"CASCADE\"})\n\tt.Index(\"user_id\", {unique: true})\n}\n"
  },
  {
    "path": "backend/persistence/migrations/20251002113000_change_tokens.down.fizz",
    "content": "drop_column(\"tokens\", \"code_verifier\")\n"
  },
  {
    "path": "backend/persistence/migrations/20251002113000_change_tokens.up.fizz",
    "content": "add_column(\"tokens\", \"code_verifier\", \"string\", {\"null\":true} )\n"
  },
  {
    "path": "backend/persistence/migrations/20251104000000_add_user_id_to_identities.down.fizz",
    "content": "drop_foreign_key(\"identities\", \"identities_users_id_fk\")\n\nsql(\"UPDATE identities SET email_id = (SELECT id FROM emails WHERE emails.user_id = identities.user_id LIMIT 1) WHERE email_id IS NULL AND user_id IS NOT NULL;\")\nchange_column(\"identities\", \"email_id\", \"uuid\", {\"null\": false})\n\ndrop_column(\"identities\", \"user_id\")\n"
  },
  {
    "path": "backend/persistence/migrations/20251104000000_add_user_id_to_identities.up.fizz",
    "content": "add_column(\"identities\", \"user_id\", \"uuid\", {\"null\": true})\n\nsql(\"UPDATE identities SET user_id = (SELECT user_id FROM emails WHERE emails.id = identities.email_id) WHERE email_id IS NOT NULL;\")\n\nchange_column(\"identities\", \"email_id\", \"uuid\", {\"null\": true})\n\nadd_foreign_key(\"identities\", \"user_id\", {\"users\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n"
  },
  {
    "path": "backend/persistence/migrations/20251119000000_change_tokens.down.fizz",
    "content": "drop_column(\"tokens\", \"link_user\")\n"
  },
  {
    "path": "backend/persistence/migrations/20251119000000_change_tokens.up.fizz",
    "content": "add_column(\"tokens\", \"link_user\", \"bool\", {\"default\": false, \"null\": false})\n"
  },
  {
    "path": "backend/persistence/migrations/20260204151021_change_user.down.fizz",
    "content": "drop_column(\"users\", \"name\")\ndrop_column(\"users\", \"family_name\")\ndrop_column(\"users\", \"given_name\")\ndrop_column(\"users\", \"picture\")\n\n"
  },
  {
    "path": "backend/persistence/migrations/20260204151021_change_user.up.fizz",
    "content": "add_column(\"users\", \"name\", \"string\", {\"null\": true})\nadd_column(\"users\", \"family_name\", \"string\", {\"null\": true})\nadd_column(\"users\", \"given_name\", \"string\", {\"null\": true})\nadd_column(\"users\", \"picture\", \"text\", {\"null\": true})\n\n"
  },
  {
    "path": "backend/persistence/models/audit_log.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6/slices\"\n\t\"github.com/gofrs/uuid\"\n)\n\ntype AuditLog struct {\n\tID                uuid.UUID    `db:\"id\" json:\"id\"`\n\tType              AuditLogType `db:\"type\" json:\"type\"`\n\tError             *string      `db:\"error\" json:\"error,omitempty\"`\n\tMetaHttpRequestId string       `db:\"meta_http_request_id\" json:\"meta_http_request_id\"`\n\tMetaSourceIp      string       `db:\"meta_source_ip\" json:\"meta_source_ip\"`\n\tMetaUserAgent     string       `db:\"meta_user_agent\" json:\"meta_user_agent\"`\n\tActorUserId       *uuid.UUID   `db:\"actor_user_id\" json:\"actor_user_id,omitempty\"`\n\tActorEmail        *string      `db:\"actor_email\" json:\"actor_email,omitempty\" mask:\"email\"`\n\tDetails           slices.Map   `db:\"details\" json:\"details\"`\n\tCreatedAt         time.Time    `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt         time.Time    `db:\"updated_at\" json:\"updated_at\"`\n}\n\ntype Details map[string]interface{}\n\ntype RequestMeta struct {\n\tHttpRequestId string\n\tSourceIp      string\n\tUserAgent     string\n}\n\nfunc NewAuditLog(auditLogType AuditLogType, requestMeta RequestMeta, details Details, user *User, logError error) (AuditLog, error) {\n\tid, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn AuditLog{}, fmt.Errorf(\"failed to create id: %w\", err)\n\t}\n\n\tauditLog := AuditLog{\n\t\tID:                id,\n\t\tType:              auditLogType,\n\t\tError:             nil,\n\t\tMetaHttpRequestId: requestMeta.HttpRequestId,\n\t\tMetaUserAgent:     requestMeta.UserAgent,\n\t\tMetaSourceIp:      requestMeta.SourceIp,\n\t\tActorUserId:       nil,\n\t\tActorEmail:        nil,\n\t\tCreatedAt:         time.Now().UTC(),\n\t\tUpdatedAt:         time.Now().UTC(),\n\t}\n\n\tif len(details) > 0 {\n\t\tauditLog.Details = slices.Map(details)\n\t}\n\n\tif user != nil {\n\t\tauditLog.ActorUserId = &user.ID\n\n\t\tif e := user.Emails.GetPrimary(); e != nil {\n\t\t\tauditLog.ActorEmail = &e.Address\n\t\t}\n\t}\n\n\tif logError != nil {\n\t\t// check if error is not nil, because else the string (formatted with fmt.Sprintf) would not be empty but look like this: `%!s(<nil>)`\n\t\ttmp := fmt.Sprintf(\"%s\", logError)\n\t\tauditLog.Error = &tmp\n\t}\n\treturn auditLog, nil\n}\n\ntype AuditLogType string\n\nvar (\n\tAuditLogUserLoggedOut AuditLogType = \"user_logged_out\"\n\n\tAuditLogPasswordSetSucceeded AuditLogType = \"password_set_succeeded\"\n\tAuditLogPasswordSetFailed    AuditLogType = \"password_set_failed\"\n\n\tAuditLogPasswordLoginSucceeded AuditLogType = \"password_login_succeeded\"\n\tAuditLogPasswordLoginFailed    AuditLogType = \"password_login_failed\"\n\n\tAuditLogPasscodeLoginInitSucceeded  AuditLogType = \"passcode_login_init_succeeded\"\n\tAuditLogPasscodeLoginInitFailed     AuditLogType = \"passcode_login_init_failed\"\n\tAuditLogPasscodeLoginFinalSucceeded AuditLogType = \"passcode_login_final_succeeded\"\n\tAuditLogPasscodeLoginFinalFailed    AuditLogType = \"passcode_login_final_failed\"\n\n\tAuditLogWebAuthnRegistrationInitSucceeded  AuditLogType = \"webauthn_registration_init_succeeded\"\n\tAuditLogWebAuthnRegistrationInitFailed     AuditLogType = \"webauthn_registration_init_failed\"\n\tAuditLogWebAuthnRegistrationFinalSucceeded AuditLogType = \"webauthn_registration_final_succeeded\"\n\tAuditLogWebAuthnRegistrationFinalFailed    AuditLogType = \"webauthn_registration_final_failed\"\n\n\tAuditLogWebAuthnAuthenticationInitSucceeded  AuditLogType = \"webauthn_authentication_init_succeeded\"\n\tAuditLogWebAuthnAuthenticationInitFailed     AuditLogType = \"webauthn_authentication_init_failed\"\n\tAuditLogWebAuthnAuthenticationFinalSucceeded AuditLogType = \"webauthn_authentication_final_succeeded\"\n\tAuditLogWebAuthnAuthenticationFinalFailed    AuditLogType = \"webauthn_authentication_final_failed\"\n\n\tAuditLogWebAuthnCredentialUpdated AuditLogType = \"webauthn_credential_updated\"\n\tAuditLogWebAuthnCredentialDeleted AuditLogType = \"webauthn_credential_deleted\"\n\n\tAuditLogThirdPartySignUpSucceeded    AuditLogType = \"thirdparty_signup_succeeded\"\n\tAuditLogThirdPartySignInSucceeded    AuditLogType = \"thirdparty_signin_succeeded\"\n\tAuditLogThirdPartyLinkingSucceeded   AuditLogType = \"thirdparty_linking_succeeded\"\n\tAuditLogThirdPartySignInSignUpFailed AuditLogType = \"thirdparty_signin_signup_failed\"\n\n\tAuditLogTokenExchangeSucceeded AuditLogType = \"token_exchange_succeeded\"\n\tAuditLogTokenExchangeFailed    AuditLogType = \"token_exchange_failed\"\n\n\t// Types used by old API and new/flow API\n\tAuditLogUserCreated         AuditLogType = \"user_created\"\n\tAuditLogEmailCreated        AuditLogType = \"email_created\"\n\tAuditLogEmailVerified       AuditLogType = \"email_verified\"\n\tAuditLogEmailDeleted        AuditLogType = \"email_deleted\"\n\tAuditLogPrimaryEmailChanged AuditLogType = \"primary_email_changed\"\n\tAuditLogUserDeleted         AuditLogType = \"user_deleted\"\n\n\t// New/flow API types\n\tAuditLogLoginSuccess             AuditLogType = \"login_success\"\n\tAuditLogLoginFailure             AuditLogType = \"login_failure\"\n\tAuditLogOTPCreated               AuditLogType = \"otp_created\"\n\tAuditLogPasskeyCreated           AuditLogType = \"passkey_created\"\n\tAuditLogPasskeyDeleted           AuditLogType = \"passkey_deleted\"\n\tAuditLogSecurityKeyCreated       AuditLogType = \"security_key_created\"\n\tAuditLogSecurityNotificationSent AuditLogType = \"security_notification_sent\"\n\tAuditLogUsernameChanged          AuditLogType = \"username_changed\"\n\tAuditLogUsernameDeleted          AuditLogType = \"username_deleted\"\n\tAuditLogPasswordChanged          AuditLogType = \"password_changed\"\n\tAuditLogPasswordDeleted          AuditLogType = \"password_deleted\"\n)\n"
  },
  {
    "path": "backend/persistence/models/email.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"slices\"\n\t\"time\"\n)\n\n// Email is used by pop to map your users database table to your go code.\ntype Email struct {\n\tID           uuid.UUID     `db:\"id\" json:\"id\"`\n\tUserID       *uuid.UUID    `db:\"user_id\" json:\"user_id,omitempty\"` // TODO: should not be a pointer anymore\n\tAddress      string        `db:\"address\" json:\"address\"`\n\tVerified     bool          `db:\"verified\" json:\"verified\"`\n\tPrimaryEmail *PrimaryEmail `has_one:\"primary_emails\" json:\"primary_emails,omitempty\"`\n\tUser         *User         `belongs_to:\"user\" json:\"user,omitempty\"`\n\tIdentities   Identities    `has_many:\"identities\" json:\"identity,omitempty\"`\n\tCreatedAt    time.Time     `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt    time.Time     `db:\"updated_at\" json:\"updated_at\"`\n}\n\ntype Emails []Email\n\nfunc NewEmail(userId *uuid.UUID, address string) *Email {\n\tid, _ := uuid.NewV4()\n\treturn &Email{\n\t\tID:           id,\n\t\tAddress:      address,\n\t\tUserID:       userId,\n\t\tVerified:     false,\n\t\tPrimaryEmail: nil,\n\t\tUser:         nil,\n\t\tCreatedAt:    time.Now(),\n\t\tUpdatedAt:    time.Now(),\n\t}\n}\n\nfunc (email *Email) IsPrimary() bool {\n\tif email.PrimaryEmail != nil && !email.PrimaryEmail.ID.IsNil() {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (email *Email) GetSamlIdentityForDomain(domain string) *SamlIdentity {\n\tfor _, identity := range email.Identities {\n\t\tif identity.SamlIdentity != nil && identity.SamlIdentity.Domain == domain {\n\t\t\treturn identity.SamlIdentity\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (emails *Emails) GetVerified() Emails {\n\tvar list Emails\n\tfor _, email := range *emails {\n\t\tif email.Verified {\n\t\t\tlist = append(list, email)\n\t\t}\n\t}\n\treturn list\n}\n\nfunc (emails *Emails) HasUnverified() bool {\n\treturn slices.ContainsFunc(*emails, func(e Email) bool {\n\t\treturn !e.Verified\n\t})\n}\n\nfunc (emails *Emails) GetPrimary() *Email {\n\tfor _, email := range *emails {\n\t\tif email.IsPrimary() {\n\t\t\treturn &email\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (emails *Emails) GetEmailByAddress(address string) *Email {\n\tfor _, email := range *emails {\n\t\tif email.Address == address {\n\t\t\treturn &email\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (emails *Emails) GetEmailById(emailId uuid.UUID) *Email {\n\tfor _, email := range *emails {\n\t\tif email.ID.String() == emailId.String() {\n\t\t\treturn &email\n\t\t}\n\t}\n\treturn nil\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (email *Email) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: email.ID},\n\t\t&validators.EmailLike{Name: \"Address\", Field: email.Address},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: email.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: email.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/flow.go",
    "content": "package models\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flowpilot\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gofrs/uuid\"\n)\n\n// Flow is used by pop to map your flows database table to your go code.\ntype Flow struct {\n\tID        uuid.UUID `json:\"id\" db:\"id\"`\n\tData      string    `json:\"data\" db:\"data\"`\n\tVersion   int       `json:\"version\" db:\"version\"`\n\tCSRFToken string    `json:\"csrf_token\" db:\"csrf_token\"`\n\tExpiresAt time.Time `json:\"expires_at\" db:\"expires_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\" db:\"updated_at\"`\n\tCreatedAt time.Time `json:\"created_at\" db:\"created_at\"`\n}\n\nfunc (f *Flow) ToFlowpilotModel() *flowpilot.FlowModel {\n\tflow := flowpilot.FlowModel{\n\t\tID:        f.ID,\n\t\tData:      f.Data,\n\t\tVersion:   f.Version,\n\t\tCSRFToken: f.CSRFToken,\n\t\tExpiresAt: f.ExpiresAt,\n\t\tCreatedAt: f.CreatedAt,\n\t\tUpdatedAt: f.UpdatedAt,\n\t}\n\n\treturn &flow\n}\n\n// Flows is not required by pop and may be deleted\ntype Flows []Flow\n\n// Validate gets run every time you call a \"pop.validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\n// This method is not required and may be deleted.\nfunc (f *Flow) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.NewErrors(), nil\n}\n\n// ValidateCreate gets run every time you call \"pop.ValidateAndCreate\" method.\n// This method is not required and may be deleted.\nfunc (f *Flow) ValidateCreate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.NewErrors(), nil\n}\n\n// ValidateUpdate gets run every time you call \"pop.ValidateAndUpdate\" method.\n// This method is not required and may be deleted.\nfunc (f *Flow) ValidateUpdate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.NewErrors(), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/identity.go",
    "content": "package models\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/pop/v6/slices\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n)\n\n// Identity is used by pop to map your identities database table to your go code.\ntype Identity struct {\n\tID             uuid.UUID     `json:\"id\" db:\"id\"`\n\tProviderUserID string        `json:\"provider_user_id\" db:\"provider_user_id\"`\n\tProviderID     string        `json:\"provider_id\" db:\"provider_id\"`\n\tData           slices.Map    `json:\"data\" db:\"data\"`\n\tEmailID        *uuid.UUID    `json:\"email_id\" db:\"email_id\"`\n\tUserID         *uuid.UUID    `json:\"user_id\" db:\"user_id\"`\n\tEmail          *Email        `json:\"email,omitempty\" belongs_to:\"email\"`\n\tCreatedAt      time.Time     `json:\"created_at\" db:\"created_at\"`\n\tUpdatedAt      time.Time     `json:\"updated_at\" db:\"updated_at\"`\n\tSamlIdentity   *SamlIdentity `json:\"saml_identity\" has_one:\"saml_identity\"`\n}\n\ntype Identities []Identity\n\nfunc (identities Identities) GetIdentity(providerID string, providerUserID string) *Identity {\n\tfor _, identity := range identities {\n\t\tif identity.ProviderID == providerID && identity.ProviderUserID == providerUserID {\n\t\t\treturn &identity\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc NewIdentity(providerID string, identityData map[string]interface{}, emailID *uuid.UUID, userID *uuid.UUID) (*Identity, error) {\n\tproviderUserID, ok := identityData[\"sub\"]\n\tif !ok {\n\t\treturn nil, errors.New(\"missing provider user id\")\n\t}\n\tnow := time.Now().UTC()\n\n\tid, _ := uuid.NewV4()\n\tidentity := &Identity{\n\t\tID:             id,\n\t\tData:           identityData,\n\t\tProviderUserID: providerUserID.(string),\n\t\tProviderID:     providerID,\n\t\tEmailID:        emailID,\n\t\tUserID:         userID,\n\t\tCreatedAt:      now,\n\t\tUpdatedAt:      now,\n\t}\n\n\treturn identity, nil\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\n// This method is not required and may be deleted.\nfunc (i *Identity) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: i.ID},\n\t\t&validators.StringIsPresent{Name: \"ProviderUserID\", Field: i.ProviderUserID},\n\t\t&validators.StringIsPresent{Name: \"ProviderID\", Field: i.ProviderID},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: i.CreatedAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: i.UpdatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/jwk.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"time\"\n)\n\ntype Jwk struct {\n\tID        int       `db:\"id\"`\n\tKeyData   string    `db:\"key_data\"`\n\tCreatedAt time.Time `db:\"created_at\"`\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (jwk *Jwk) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.StringIsPresent{Name: \"KeyData\", Field: jwk.KeyData},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: jwk.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/otp_secret.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype OTPSecret struct {\n\tID        uuid.UUID `db:\"id\"`\n\tUserID    uuid.UUID `db:\"user_id\"`\n\tSecret    string    `db:\"secret\"`\n\tCreatedAt time.Time `db:\"created_at\"`\n\tUpdatedAt time.Time `db:\"updated_at\"`\n}\n\nfunc (otpSecret OTPSecret) TableName() string {\n\treturn \"otp_secrets\"\n}\n\nfunc NewOTPSecret(userID uuid.UUID, secret string) *OTPSecret {\n\tid, _ := uuid.NewV4()\n\treturn &OTPSecret{\n\t\tID:        id,\n\t\tUserID:    userID,\n\t\tSecret:    secret,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t}\n}\n\nfunc (otpSecret *OTPSecret) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: otpSecret.ID},\n\t\t&validators.UUIDIsPresent{Name: \"UserID\", Field: otpSecret.UserID},\n\t\t&validators.StringIsPresent{Name: \"Secret\", Field: otpSecret.Secret},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: otpSecret.CreatedAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: otpSecret.UpdatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/passcode.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\n// Passcode is used by pop to map your passcodes database table to your go code.\ntype Passcode struct {\n\tID        uuid.UUID  `db:\"id\"`\n\tUserId    *uuid.UUID `db:\"user_id\"`\n\tEmailID   *uuid.UUID `db:\"email_id\"`\n\tTtl       int        `db:\"ttl\"` // in seconds\n\tCode      string     `db:\"code\"`\n\tTryCount  int        `db:\"try_count\"`\n\tCreatedAt time.Time  `db:\"created_at\"`\n\tUpdatedAt time.Time  `db:\"updated_at\"`\n\tEmail     Email      `belongs_to:\"email\"`\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (passcode *Passcode) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: passcode.ID},\n\t\t&validators.StringLengthInRange{Name: \"Code\", Field: passcode.Code, Min: 6},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: passcode.CreatedAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: passcode.UpdatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/password_credential.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype PasswordCredential struct {\n\tID        uuid.UUID `db:\"id\"`\n\tUserId    uuid.UUID `db:\"user_id\"`\n\tPassword  string    `db:\"password\"`\n\tCreatedAt time.Time `db:\"created_at\"`\n\tUpdatedAt time.Time `db:\"updated_at\"`\n}\n\nfunc NewPasswordCredential(userId uuid.UUID, password string) *PasswordCredential {\n\tid, _ := uuid.NewV4()\n\treturn &PasswordCredential{\n\t\tID:        id,\n\t\tUserId:    userId,\n\t\tPassword:  password,\n\t\tCreatedAt: time.Now().UTC(),\n\t\tUpdatedAt: time.Now().UTC(),\n\t}\n}\n\nfunc (password *PasswordCredential) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.StringIsPresent{Name: \"Password\", Field: password.Password},\n\t\t&validators.UUIDIsPresent{Name: \"UserId\", Field: password.UserId},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/primary_email.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype PrimaryEmail struct {\n\tID        uuid.UUID `db:\"id\" json:\"id\"`\n\tEmailID   uuid.UUID `db:\"email_id\" json:\"email_id\"`\n\tUserID    uuid.UUID `db:\"user_id\" json:\"-\"`\n\tEmail     *Email    `belongs_to:\"email\" json:\"email\"`\n\tUser      *User     `belongs_to:\"user\" json:\"-\"`\n\tCreatedAt time.Time `db:\"created_at\" json:\"-\"`\n\tUpdatedAt time.Time `db:\"updated_at\" json:\"-\"`\n}\n\nfunc NewPrimaryEmail(emailId uuid.UUID, userId uuid.UUID) *PrimaryEmail {\n\tid, _ := uuid.NewV4()\n\n\treturn &PrimaryEmail{\n\t\tID:        id,\n\t\tEmailID:   emailId,\n\t\tUserID:    userId,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t}\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (primaryEmail *PrimaryEmail) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: primaryEmail.ID},\n\t\t&validators.UUIDIsPresent{Name: \"EmailID\", Field: primaryEmail.EmailID},\n\t\t&validators.UUIDIsPresent{Name: \"UserID\", Field: primaryEmail.UserID},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: primaryEmail.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: primaryEmail.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/saml_certificate.go",
    "content": "package models\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/aes_gcm\"\n\t\"math/big\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gofrs/uuid\"\n)\n\n// SamlCertificate is used by pop to map your saml_certs database table to your go code.\ntype SamlCertificate struct {\n\tID            uuid.UUID `json:\"id\" db:\"id\"`\n\tCertData      string    `json:\"cert_data\" db:\"cert_data\"`\n\tCertKey       string    `json:\"cert_key\" db:\"cert_key\"`\n\tEncryptionKey string    `json:\"encryption_key\" db:\"encryption_key\"`\n\tCreatedAt     time.Time `json:\"created_at\" db:\"created_at\"`\n\tUpdatedAt     time.Time `json:\"updated_at\" db:\"updated_at\"`\n}\n\nfunc createTemplate(serviceName string, creationTime time.Time) *x509.Certificate {\n\treturn &x509.Certificate{\n\t\tSerialNumber: big.NewInt(1),\n\t\tSubject: pkix.Name{\n\t\t\tCommonName: serviceName,\n\t\t},\n\t\tNotBefore:             creationTime,\n\t\tNotAfter:              creationTime.Add(365 * 24 * time.Hour), // Valid for 1 year\n\t\tKeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},\n\t\tBasicConstraintsValid: true,\n\t}\n}\n\nfunc GenerateCertificate(serviceName string, privateKey *rsa.PrivateKey, currentTime time.Time) (string, error) {\n\ttemplate := createTemplate(serviceName, currentTime)\n\tcert, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tcertPem := pem.EncodeToMemory(&pem.Block{\n\t\tType:  \"CERTIFICATE\",\n\t\tBytes: cert,\n\t})\n\n\treturn string(certPem), nil\n}\n\nfunc encryptPrivateKey(privateKey []byte, encryptionKey string) (string, error) {\n\tgcm, err := aes_gcm.NewAESGCM([]string{encryptionKey})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tencryptedKey, err := gcm.Encrypt(privateKey)\n\n\treturn encryptedKey, nil\n}\n\nfunc NewSamlCertificate(serviceName string) (*SamlCertificate, error) {\n\tid, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not generate id: %w\", err)\n\t}\n\n\tnow := time.Now()\n\n\tprivateKey, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to generate private key: %w\", err)\n\t}\n\n\tprivateKeyPEM := x509.MarshalPKCS1PrivateKey(privateKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error encoding private key: %w\", err)\n\n\t}\n\tprivateKeyPEMBlock := pem.EncodeToMemory(&pem.Block{Type: \"RSA PRIVATE KEY\", Bytes: privateKeyPEM})\n\n\tencryptionKey, err := crypto.GenerateRandomStringURLSafe(32)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to create encryptionKey: %w\", err)\n\t}\n\n\tencryptedPrivateKey, err := encryptPrivateKey(privateKeyPEMBlock, encryptionKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to encrypt private key: %w\", err)\n\t}\n\n\tcert, err := GenerateCertificate(serviceName, privateKey, now)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to create certificate: %w\", err)\n\t}\n\n\treturn &SamlCertificate{\n\t\tID:            id,\n\t\tCertData:      cert,\n\t\tCertKey:       encryptedPrivateKey,\n\t\tEncryptionKey: encryptionKey,\n\t\tCreatedAt:     now,\n\t\tUpdatedAt:     now,\n\t}, nil\n}\n\nfunc (s *SamlCertificate) DecryptCertKey() ([]byte, error) {\n\tgcm, err := aes_gcm.NewAESGCM([]string{s.EncryptionKey})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tencryptedKey, err := gcm.Decrypt(s.CertKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn encryptedKey, nil\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\n// This method is not required and may be deleted.\nfunc (s *SamlCertificate) Validate(_ *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: s.ID},\n\t\t&validators.StringIsPresent{Name: \"CertData\", Field: s.CertData},\n\t\t&validators.StringIsPresent{Name: \"CertKey\", Field: s.CertKey},\n\t\t&validators.StringIsPresent{Name: \"EncryptionKey\", Field: s.EncryptionKey},\n\t\t&validators.StringLengthInRange{\n\t\t\tName:  \"EncryptionKey\",\n\t\t\tField: s.EncryptionKey,\n\t\t\tMin:   32,\n\t\t},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/saml_identity.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype SamlIdentity struct {\n\tID         uuid.UUID `json:\"id\" db:\"id\"`\n\tIdentityID uuid.UUID `json:\"identity_id\" db:\"identity_id\"`\n\tDomain     string    `json:\"domain\" db:\"domain\"`\n\tCreatedAt  time.Time `json:\"created_at\" db:\"created_at\"`\n\tUpdatedAt  time.Time `json:\"updated_at\" db:\"updated_at\"`\n}\n\ntype SamlIdentities []SamlIdentity\n\nfunc (i *SamlIdentity) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: i.ID},\n\t\t&validators.UUIDIsPresent{Name: \"IdentityID\", Field: i.IdentityID},\n\t\t&validators.StringIsPresent{Name: \"Domain\", Field: i.Domain},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/saml_idp_initiated_request.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype SamlIDPInitiatedRequest struct {\n\tID         uuid.UUID `db:\"id\"`\n\tResponseID string    `db:\"response_id\"`\n\tIssuer     string    `db:\"issuer\"`\n\tExpiresAt  time.Time `db:\"expires_at\"`\n\tCreatedAt  time.Time `db:\"created_at\"`\n}\n\nfunc NewSamlIDPInitiatedRequest(responseID, issuer string, expiresAt time.Time) (*SamlIDPInitiatedRequest, error) {\n\tid, _ := uuid.NewV4()\n\n\treturn &SamlIDPInitiatedRequest{\n\t\tID:         id,\n\t\tResponseID: responseID,\n\t\tIssuer:     issuer,\n\t\tExpiresAt:  expiresAt,\n\t\tCreatedAt:  time.Now().UTC(),\n\t}, nil\n}\n\nfunc (samlIDPInitiatedRequest SamlIDPInitiatedRequest) TableName() string {\n\treturn \"saml_idp_initiated_requests\"\n}\n\nfunc (r *SamlIDPInitiatedRequest) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: r.ID},\n\t\t&validators.StringIsPresent{Name: \"ResponseID\", Field: r.ResponseID},\n\t\t&validators.StringIsPresent{Name: \"Issuer\", Field: r.Issuer},\n\t\t&validators.TimeIsPresent{Name: \"ExpiresAt\", Field: r.ExpiresAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: r.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/saml_state.go",
    "content": "package models\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype SamlState struct {\n\tID        uuid.UUID `db:\"id\"`\n\tNonce     string    `db:\"nonce\"`\n\tState     string    `db:\"state\"`\n\tExpiresAt time.Time `db:\"expires_at\"`\n\tCreatedAt time.Time `db:\"created_at\"`\n\tUpdatedAt time.Time `db:\"updated_at\"`\n}\n\nfunc NewSamlState(nonce string, state string) (*SamlState, error) {\n\tif strings.TrimSpace(nonce) == \"\" {\n\t\treturn nil, errors.New(\"nonce is required\")\n\t}\n\n\tif strings.TrimSpace(state) == \"\" {\n\t\treturn nil, errors.New(\"state is required\")\n\t}\n\n\tnow := time.Now().UTC()\n\n\tid, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not generate id: %w\", err)\n\t}\n\n\treturn &SamlState{\n\t\tID:        id,\n\t\tNonce:     nonce,\n\t\tState:     state,\n\t\tExpiresAt: now.Add(time.Minute),\n\t\tCreatedAt: now,\n\t\tUpdatedAt: now,\n\t}, nil\n}\n"
  },
  {
    "path": "backend/persistence/models/session.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype Session struct {\n\tID        uuid.UUID    `db:\"id\" json:\"id\"`\n\tUserID    uuid.UUID    `db:\"user_id\" json:\"user_id\"`\n\tUserAgent nulls.String `db:\"user_agent\" json:\"user_agent\"`\n\tIpAddress nulls.String `db:\"ip_address\" json:\"ip_address\"`\n\tCreatedAt time.Time    `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt time.Time    `db:\"updated_at\" json:\"updated_at\"`\n\tExpiresAt *time.Time   `db:\"expires_at\" json:\"expires_at\"`\n\tLastUsed  time.Time    `db:\"last_used\" json:\"last_used\"`\n}\n\nfunc (session *Session) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: session.ID},\n\t\t&validators.UUIDIsPresent{Name: \"UserID\", Field: session.UserID},\n\t\t&validators.TimeIsPresent{Name: \"LastUsed\", Field: session.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: session.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: session.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/token.go",
    "content": "package models\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto\"\n)\n\ntype Token struct {\n\tID               uuid.UUID  `db:\"id\"`\n\tUserID           uuid.UUID  `db:\"user_id\"`\n\tIdentityID       *uuid.UUID `db:\"identity_id\"`\n\tIsFlow           bool       `db:\"is_flow\"`\n\tValue            string     `db:\"value\"`\n\tUserCreated      bool       `db:\"user_created\"`\n\tPKCECodeVerifier *string    `db:\"code_verifier\"`\n\tLinkUser         bool       `db:\"link_user\"`\n\tExpiresAt        time.Time  `db:\"expires_at\"`\n\tCreatedAt        time.Time  `db:\"created_at\"`\n\tUpdatedAt        time.Time  `db:\"updated_at\"`\n}\n\nfunc TokenWithIdentityID(identityID uuid.UUID) func(*Token) {\n\treturn func(token *Token) {\n\t\ttoken.IdentityID = &identityID\n\t}\n}\n\nfunc TokenForFlowAPI(isFlow bool) func(*Token) {\n\treturn func(token *Token) {\n\t\ttoken.IsFlow = isFlow\n\t}\n}\n\nfunc TokenUserCreated(userCreated bool) func(*Token) {\n\treturn func(token *Token) {\n\t\ttoken.UserCreated = userCreated\n\t}\n}\n\nfunc TokenPKCESessionVerifier(pkceSessionVerifier string) func(*Token) {\n\treturn func(token *Token) {\n\t\ttoken.PKCECodeVerifier = &pkceSessionVerifier\n\t}\n}\n\nfunc TokenWithLinkUser(linkUser bool) func(*Token) {\n\treturn func(token *Token) {\n\t\ttoken.LinkUser = linkUser\n\t}\n}\n\nfunc NewToken(userID uuid.UUID, options ...func(*Token)) (*Token, error) {\n\tif userID.IsNil() {\n\t\treturn nil, errors.New(\"userID is required\")\n\t}\n\n\tnow := time.Now().UTC()\n\n\tid, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not generate id: %w\", err)\n\t}\n\n\tvalue, err := crypto.GenerateRandomStringURLSafe(32)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not generate random string: %w\", err)\n\t}\n\n\ttoken := &Token{\n\t\tID:        id,\n\t\tUserID:    userID,\n\t\tValue:     value,\n\t\tExpiresAt: now.Add(time.Minute),\n\t\tCreatedAt: now,\n\t\tUpdatedAt: now,\n\t}\n\n\tfor _, option := range options {\n\t\toption(token)\n\t}\n\n\treturn token, nil\n}\n\nfunc (token *Token) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: token.ID},\n\t\t&validators.UUIDIsPresent{Name: \"UserID\", Field: token.UserID},\n\t\t&validators.StringIsPresent{Name: \"Value\", Field: token.Value},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: token.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: token.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/trusted_device.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype TrustedDevice struct {\n\tID          uuid.UUID `db:\"id\"`\n\tUserID      uuid.UUID `db:\"user_id\"`\n\tDeviceToken string    `db:\"device_token\"`\n\tExpiresAt   time.Time `db:\"expires_at\"`\n\tCreatedAt   time.Time `db:\"created_at\"`\n\tUpdatedAt   time.Time `db:\"updated_at\"`\n}\n\nfunc (trustedDevice *TrustedDevice) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: trustedDevice.ID},\n\t\t&validators.UUIDIsPresent{Name: \"UserID\", Field: trustedDevice.UserID},\n\t\t&validators.StringIsPresent{Name: \"DeviceToken\", Field: trustedDevice.DeviceToken},\n\t\t&validators.StringLengthInRange{Name: \"DeviceToken\", Field: trustedDevice.DeviceToken, Min: 64, Max: 128},\n\t\t&validators.TimeIsPresent{Name: \"ExpiresAt\", Field: trustedDevice.ExpiresAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: trustedDevice.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: trustedDevice.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/user.go",
    "content": "package models\n\nimport (\n\t\"encoding/base64\"\n\t\"time\"\n\n\t\"slices\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n)\n\n// ProviderProfile is a provider-agnostic projection of \"profile-like\" data.\n// Keep this in models so other packages can map their data into it without creating import cycles.\ntype ProviderProfile struct {\n\tName       string\n\tGivenName  string\n\tFamilyName string\n\tPicture    string\n}\n\n// User is used by pop to map your users database table to your go code.\ntype User struct {\n\tID                  uuid.UUID           `db:\"id\" json:\"id\"`\n\tWebauthnCredentials WebauthnCredentials `has_many:\"webauthn_credentials\" json:\"webauthn_credentials,omitempty\"`\n\tEmails              Emails              `has_many:\"emails\" json:\"-\"`\n\tCreatedAt           time.Time           `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt           time.Time           `db:\"updated_at\" json:\"updated_at\"`\n\tUsername            *Username           `has_one:\"username\" json:\"username,omitempty\"`\n\tOTPSecret           *OTPSecret          `has_one:\"otp_secret\" json:\"-\"`\n\tPasswordCredential  *PasswordCredential `has_one:\"password_credentials\" json:\"-\"`\n\tMetadata            *UserMetadata       `has_one:\"user_metadata\" json:\"-\"`\n\tIdentities          Identities          `has_many:\"identities\" json:\"-\"`\n\tName                nulls.String        `db:\"name\" json:\"name\"`\n\tGivenName           nulls.String        `db:\"given_name\" json:\"given_name\"`\n\tFamilyName          nulls.String        `db:\"family_name\" json:\"family_name\"`\n\tPicture             nulls.String        `db:\"picture\" json:\"picture\"`\n}\n\nfunc (user *User) DeleteWebauthnCredential(credentialId string) {\n\tfor i := range user.WebauthnCredentials {\n\t\tif user.WebauthnCredentials[i].ID == credentialId {\n\t\t\tuser.WebauthnCredentials = slices.Delete(user.WebauthnCredentials, i, i+1)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (user *User) GetIdentities() Identities {\n\tvar identities Identities\n\tfor _, email := range user.Emails {\n\t\tidentities = append(identities, email.Identities...)\n\t}\n\treturn identities\n}\n\nfunc NewUser() User {\n\tid, _ := uuid.NewV4()\n\treturn User{\n\t\tID:        id,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t}\n}\n\nfunc (user *User) GetUsername() *string {\n\tif user.Username != nil {\n\t\treturn &user.Username.Username\n\t}\n\treturn nil\n}\n\nfunc (user *User) SetUsername(username *Username) {\n\tuser.Username = username\n}\n\nfunc (user *User) DeleteUsername() {\n\tuser.Username = nil\n}\n\nfunc (user *User) SetPrimaryEmail(primary *PrimaryEmail) {\n\tfor i := range user.Emails {\n\t\tif user.Emails[i].ID.String() == primary.EmailID.String() {\n\t\t\tuser.Emails[i].PrimaryEmail = primary\n\t\t} else {\n\t\t\tuser.Emails[i].PrimaryEmail = nil\n\t\t}\n\t}\n}\n\nfunc (user *User) UpdateEmail(email Email) {\n\tfor i := range user.Emails {\n\t\tif user.Emails[i].ID.String() == email.ID.String() {\n\t\t\tuser.Emails[i] = email\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (user *User) DeleteEmail(email Email) {\n\tfor i := range user.Emails {\n\t\tif user.Emails[i].ID.String() == email.ID.String() {\n\t\t\tuser.Emails = slices.Delete(user.Emails, i, i+1)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (user *User) DeleteOTPSecret() {\n\tuser.OTPSecret = nil\n}\n\nfunc (user *User) GetEmailById(emailId uuid.UUID) *Email {\n\treturn user.Emails.GetEmailById(emailId)\n}\n\nfunc (user *User) GetEmailByAddress(address string) *Email {\n\treturn user.Emails.GetEmailByAddress(address)\n}\n\nfunc (user *User) GetWebauthnCredentialById(credentialId string) *WebauthnCredential {\n\tfor i := range user.WebauthnCredentials {\n\t\tif user.WebauthnCredentials[i].ID == credentialId {\n\t\t\treturn &user.WebauthnCredentials[i]\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (user *User) GetPasskeys() WebauthnCredentials {\n\tcredentials := make(WebauthnCredentials, 0)\n\tfor _, credential := range user.WebauthnCredentials {\n\t\tif credential.MFAOnly == false {\n\t\t\tcredentials = append(credentials, credential)\n\t\t}\n\t}\n\treturn credentials\n}\n\nfunc (user *User) GetSecurityKeys() WebauthnCredentials {\n\tcredentials := make(WebauthnCredentials, 0)\n\tfor _, credential := range user.WebauthnCredentials {\n\t\tif credential.MFAOnly == true {\n\t\t\tcredentials = append(credentials, credential)\n\t\t}\n\t}\n\treturn credentials\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (user *User) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: user.ID},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: user.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: user.CreatedAt},\n\t), nil\n}\n\nfunc (user *User) WebAuthnID() []byte {\n\treturn user.ID.Bytes()\n}\n\nfunc (user *User) WebAuthnName() string {\n\temail := user.Emails.GetPrimary()\n\tif email != nil {\n\t\treturn email.Address\n\t}\n\treturn \"username\" // TODO\n}\n\nfunc (user *User) WebAuthnDisplayName() string {\n\temail := user.Emails.GetPrimary()\n\tif email != nil {\n\t\treturn email.Address\n\t}\n\treturn \"username\" // TODO\n}\n\nfunc (user *User) WebAuthnIcon() string {\n\treturn \"\"\n}\n\nfunc (user *User) WebAuthnCredentials() []webauthn.Credential {\n\tvar credentials []webauthn.Credential\n\n\tfor _, credential := range user.WebauthnCredentials {\n\t\tcredentialID, _ := base64.RawURLEncoding.DecodeString(credential.ID)\n\t\tpublicKey, _ := base64.RawURLEncoding.DecodeString(credential.PublicKey)\n\n\t\ttransport := make([]protocol.AuthenticatorTransport, len(credential.Transports))\n\n\t\tfor i, t := range credential.Transports {\n\t\t\ttransport[i] = protocol.AuthenticatorTransport(t.Name)\n\t\t}\n\n\t\tc := webauthn.Credential{\n\t\t\tID:              credentialID,\n\t\t\tPublicKey:       publicKey,\n\t\t\tAttestationType: credential.AttestationType,\n\t\t\tAuthenticator: webauthn.Authenticator{\n\t\t\t\tAAGUID:    credential.AAGUID.Bytes(),\n\t\t\t\tSignCount: uint32(credential.SignCount),\n\t\t\t},\n\t\t\tTransport: transport,\n\t\t}\n\n\t\tcredentials = append(credentials, c)\n\t}\n\n\treturn credentials\n}\n\n// SyncFromProviderProfile overwrites fields only if the incoming provider value is non-empty.\n// If the incoming value is empty, the existing value is kept.\nfunc (user *User) SyncFromProviderProfile(profile ProviderProfile) (changed bool) {\n\tif profile.Name != \"\" {\n\t\tnewName := nulls.NewString(profile.Name)\n\t\tif user.Name != newName {\n\t\t\tuser.Name = newName\n\t\t\tchanged = true\n\t\t}\n\t}\n\n\tif profile.GivenName != \"\" {\n\t\tnewGiven := nulls.NewString(profile.GivenName)\n\t\tif user.GivenName != newGiven {\n\t\t\tuser.GivenName = newGiven\n\t\t\tchanged = true\n\t\t}\n\t}\n\n\tif profile.FamilyName != \"\" {\n\t\tnewFamily := nulls.NewString(profile.FamilyName)\n\t\tif user.FamilyName != newFamily {\n\t\t\tuser.FamilyName = newFamily\n\t\t\tchanged = true\n\t\t}\n\t}\n\n\tif profile.Picture != \"\" {\n\t\tnewPicture := nulls.NewString(profile.Picture)\n\t\tif user.Picture != newPicture {\n\t\t\tuser.Picture = newPicture\n\t\t\tchanged = true\n\t\t}\n\t}\n\n\treturn changed\n}\n"
  },
  {
    "path": "backend/persistence/models/user_metadata.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype UserMetadata struct {\n\tID        uuid.UUID    `db:\"id\"`\n\tUserID    uuid.UUID    `db:\"user_id\"`\n\tPublic    nulls.String `db:\"public_metadata\"`\n\tPrivate   nulls.String `db:\"private_metadata\"`\n\tUnsafe    nulls.String `db:\"unsafe_metadata\"`\n\tCreatedAt time.Time    `db:\"created_at\"`\n\tUpdatedAt time.Time    `db:\"updated_at\"`\n}\n\nfunc (m *UserMetadata) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\tmetadataMax := 3000\n\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: m.ID},\n\t\t&validators.UUIDIsPresent{Name: \"UserID\", Field: m.UserID},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: m.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: m.CreatedAt},\n\t\t&validators.StringLengthInRange{\n\t\t\tName:    \"Public\",\n\t\t\tField:   m.Public.String,\n\t\t\tMax:     metadataMax,\n\t\t\tMessage: fmt.Sprintf(\"public metadata must not exceed %d characters\", metadataMax),\n\t\t},\n\t\t&validators.StringLengthInRange{\n\t\t\tName:    \"Private\",\n\t\t\tField:   m.Private.String,\n\t\t\tMax:     metadataMax,\n\t\t\tMessage: fmt.Sprintf(\"private metadata must not exceed %d characters\", metadataMax),\n\t\t},\n\t\t&validators.StringLengthInRange{\n\t\t\tName:    \"Unsafe\",\n\t\t\tField:   m.Unsafe.String,\n\t\t\tMax:     metadataMax,\n\t\t\tMessage: fmt.Sprintf(\"unsafe metadata must not exceed %d characters\", metadataMax),\n\t\t},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/username.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype Username struct {\n\tID        uuid.UUID `db:\"id\"`\n\tUserId    uuid.UUID `db:\"user_id\"`\n\tUsername  string    `db:\"username\"`\n\tCreatedAt time.Time `db:\"created_at\"`\n\tUpdatedAt time.Time `db:\"updated_at\"`\n}\n\nfunc NewUsername(userId uuid.UUID, username string) *Username {\n\tid, _ := uuid.NewV4()\n\treturn &Username{\n\t\tID:        id,\n\t\tUserId:    userId,\n\t\tUsername:  username,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t}\n}\n\nfunc (username *Username) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.StringIsPresent{Name: \"Username\", Field: username.Username},\n\t\t&validators.UUIDIsPresent{Name: \"UserId\", Field: username.UserId},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/webauthn_credential.go",
    "content": "package models\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\n// WebauthnCredential is used by pop to map your webauthn_credentials database table to your go code.\ntype WebauthnCredential struct {\n\tID              string                        `db:\"id\" json:\"id\"`\n\tName            *string                       `db:\"name\" json:\"name\"`\n\tUserId          uuid.UUID                     `db:\"user_id\" json:\"user_id\"`\n\tPublicKey       string                        `db:\"public_key\" json:\"public_key\"`\n\tAttestationType string                        `db:\"attestation_type\" json:\"attestation_type\"`\n\tAAGUID          uuid.UUID                     `db:\"aaguid\" json:\"aaguid\"`\n\tSignCount       int                           `db:\"sign_count\" json:\"sign_count\"`\n\tLastUsedAt      *time.Time                    `db:\"last_used_at\" json:\"last_used_at\"`\n\tCreatedAt       time.Time                     `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt       time.Time                     `db:\"updated_at\" json:\"updated_at\"`\n\tTransports      Transports                    `has_many:\"webauthn_credential_transports\" json:\"transports\"`\n\tBackupEligible  bool                          `db:\"backup_eligible\" json:\"backup_eligible\"`\n\tBackupState     bool                          `db:\"backup_state\" json:\"backup_state\"`\n\tMFAOnly         bool                          `db:\"mfa_only\" json:\"mfa_only\"`\n\tUserHandleID    *uuid.UUID                    `db:\"user_handle_id\" json:\"-\"`\n\tUserHandle      *WebauthnCredentialUserHandle `belongs_to:\"webauthn_credential_user_handle\" fk_id:\"webauthn_credential_user_handle_fkey\" json:\"user_handle,omitempty\"`\n}\n\ntype WebauthnCredentials []WebauthnCredential\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (credential *WebauthnCredential) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.StringIsPresent{Name: \"ID\", Field: credential.ID},\n\t\t&validators.UUIDIsPresent{Name: \"UserId\", Field: credential.UserId},\n\t\t&validators.StringIsPresent{Name: \"PublicKey\", Field: credential.PublicKey},\n\t\t&validators.IntIsGreaterThan{Name: \"SignCount\", Field: credential.SignCount, Compared: -1},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: credential.CreatedAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: credential.UpdatedAt},\n\t), nil\n}\n\nfunc (credential *WebauthnCredential) GetWebauthnTransports() []protocol.AuthenticatorTransport {\n\ttransports := make([]protocol.AuthenticatorTransport, len(credential.Transports))\n\tfor i, transport := range credential.Transports {\n\t\ttransports[i] = protocol.AuthenticatorTransport(transport.Name)\n\t}\n\treturn transports\n}\n\nfunc (credential *WebauthnCredential) GetWebauthnDescriptor() (*protocol.CredentialDescriptor, error) {\n\tid, err := base64.RawURLEncoding.DecodeString(credential.ID)\n\tif err != nil {\n\t\tfmt.Println(\"failed to decode the credential id\", err)\n\t\treturn nil, err\n\t}\n\n\treturn &protocol.CredentialDescriptor{\n\t\tType:            protocol.PublicKeyCredentialType,\n\t\tCredentialID:    id,\n\t\tTransport:       credential.GetWebauthnTransports(),\n\t\tAttestationType: credential.AttestationType,\n\t}, nil\n}\n\nfunc (credentials WebauthnCredentials) GetWebauthnDescriptors() ([]protocol.CredentialDescriptor, error) {\n\tdescriptors := make([]protocol.CredentialDescriptor, len(credentials))\n\tfor i, credential := range credentials {\n\t\tdescriptor, err := credential.GetWebauthnDescriptor()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdescriptors[i] = *descriptor\n\t}\n\treturn descriptors, nil\n}\n"
  },
  {
    "path": "backend/persistence/models/webauthn_credential_transport.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n)\n\n// WebauthnCredentialTransport is used by pop to map your webauthn_credential_transport table to your go code.\ntype WebauthnCredentialTransport struct {\n\tID                   uuid.UUID           `db:\"id\"`\n\tName                 string              `db:\"name\"`\n\tWebauthnCredentialID string              `db:\"webauthn_credential_id\"`\n\tWebauthnCredential   *WebauthnCredential `belongs_to:\"webauthn_credential\"`\n}\n\ntype Transports []WebauthnCredentialTransport\n\nfunc (transports Transports) GetNames() []string {\n\tnames := make([]string, len(transports))\n\tfor i, t := range transports {\n\t\tnames[i] = t.Name\n\t}\n\treturn names\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (transport *WebauthnCredentialTransport) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: transport.ID},\n\t\t&validators.StringIsPresent{Name: \"WebauthnCredentialID\", Field: transport.WebauthnCredentialID},\n\t\t&validators.StringIsPresent{Name: \"Name\", Field: transport.Name},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/webauthn_credential_user_handle.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype WebauthnCredentialUserHandle struct {\n\tID        uuid.UUID `db:\"id\" json:\"id\"`\n\tUserID    uuid.UUID `db:\"user_id\" json:\"user_id\"`\n\tHandle    string    `db:\"handle\" json:\"handle\"`\n\tCreatedAt time.Time `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt time.Time `db:\"updated_at\" json:\"updated_at\"`\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (userHandle *WebauthnCredentialUserHandle) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: userHandle.ID},\n\t\t&validators.UUIDIsPresent{Name: \"UserId\", Field: userHandle.UserID},\n\t\t&validators.StringIsPresent{Name: \"handle\", Field: userHandle.Handle},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: userHandle.CreatedAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: userHandle.UpdatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/webauthn_session_data.go",
    "content": "package models\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gobuffalo/nulls\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype Operation string\n\nvar (\n\tWebauthnOperationRegistration   Operation = \"registration\"\n\tWebauthnOperationAuthentication Operation = \"authentication\"\n)\n\n// WebauthnSessionData is used by pop to map your webauthn_session_data database table to your go code.\ntype WebauthnSessionData struct {\n\tID                 uuid.UUID                              `db:\"id\"`\n\tChallenge          string                                 `db:\"challenge\"`\n\tUserId             uuid.UUID                              `db:\"user_id\"`\n\tUserVerification   string                                 `db:\"user_verification\"`\n\tCreatedAt          time.Time                              `db:\"created_at\"`\n\tUpdatedAt          time.Time                              `db:\"updated_at\"`\n\tOperation          Operation                              `db:\"operation\"`\n\tAllowedCredentials []WebauthnSessionDataAllowedCredential `has_many:\"webauthn_session_data_allowed_credentials\"`\n\tExpiresAt          nulls.Time                             `db:\"expires_at\"`\n}\n\nfunc (sd *WebauthnSessionData) decodeAllowedCredentials() [][]byte {\n\tvar allowedCredentials [][]byte\n\n\tfor _, credential := range sd.AllowedCredentials {\n\t\tcredentialId, err := base64.RawURLEncoding.DecodeString(credential.CredentialId)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tallowedCredentials = append(allowedCredentials, credentialId)\n\t}\n\n\treturn allowedCredentials\n}\n\nfunc NewWebauthnSessionDataFrom(sessionData *webauthn.SessionData, operation Operation) (*WebauthnSessionData, error) {\n\tnow := time.Now().UTC()\n\n\tsessionDataID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to generate a new uuid for session data: %w\", err)\n\t}\n\n\tuserID, _ := uuid.FromBytes(sessionData.UserID)\n\n\tallowedCredentials := make([]WebauthnSessionDataAllowedCredential, len(sessionData.AllowedCredentialIDs))\n\n\tfor index, credentialID := range sessionData.AllowedCredentialIDs {\n\t\tallowedCredentialID, err := uuid.NewV4()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to generate a uuid for the allowed credential: %w\", err)\n\t\t}\n\n\t\tallowedCredential := WebauthnSessionDataAllowedCredential{\n\t\t\tID:                    allowedCredentialID,\n\t\t\tCredentialId:          base64.RawURLEncoding.EncodeToString(credentialID),\n\t\t\tWebauthnSessionDataID: sessionDataID,\n\t\t\tCreatedAt:             now,\n\t\t\tUpdatedAt:             now,\n\t\t}\n\n\t\tallowedCredentials[index] = allowedCredential\n\t}\n\n\tsessionDataModel := &WebauthnSessionData{\n\t\tID:                 sessionDataID,\n\t\tChallenge:          sessionData.Challenge,\n\t\tUserId:             userID,\n\t\tUserVerification:   string(sessionData.UserVerification),\n\t\tCreatedAt:          now,\n\t\tUpdatedAt:          now,\n\t\tOperation:          operation,\n\t\tAllowedCredentials: allowedCredentials,\n\t\tExpiresAt:          nulls.NewTime(sessionData.Expires.UTC()),\n\t}\n\n\treturn sessionDataModel, nil\n}\n\nfunc (sd *WebauthnSessionData) ToSessionData() *webauthn.SessionData {\n\tallowedCredentials := sd.decodeAllowedCredentials()\n\n\t// TODO: do we need the following lines and is the user optional?\n\tvar userId []byte = nil\n\n\tif !sd.UserId.IsNil() {\n\t\tuserId = sd.UserId.Bytes()\n\t}\n\n\tsessionData := &webauthn.SessionData{\n\t\tChallenge:            sd.Challenge,\n\t\tUserID:               userId,\n\t\tAllowedCredentialIDs: allowedCredentials,\n\t\tUserVerification:     protocol.UserVerificationRequirement(sd.UserVerification),\n\t\tExpires:              sd.ExpiresAt.Time,\n\t}\n\n\treturn sessionData\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (sd *WebauthnSessionData) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: sd.ID},\n\t\t&validators.StringIsPresent{Name: \"Challenge\", Field: sd.Challenge},\n\t\t&validators.StringInclusion{Name: \"Operation\", Field: string(sd.Operation), List: []string{string(WebauthnOperationRegistration), string(WebauthnOperationAuthentication)}},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: sd.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: sd.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/webauthn_session_data_allowed_credential.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\n// WebauthnSessionDataAllowedCredential is used by pop to map your webauthn_session_data_allowed_credential database table to your go code.\ntype WebauthnSessionDataAllowedCredential struct {\n\tID                    uuid.UUID            `db:\"id\"`\n\tCredentialId          string               `db:\"credential_id\"`\n\tWebauthnSessionDataID uuid.UUID            `db:\"webauthn_session_data_id\"`\n\tCreatedAt             time.Time            `db:\"created_at\"`\n\tUpdatedAt             time.Time            `db:\"updated_at\"`\n\tWebauthnSessionData   *WebauthnSessionData `belongs_to:\"webauthn_session_data\"`\n}\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\nfunc (credential *WebauthnSessionDataAllowedCredential) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: credential.ID},\n\t\t&validators.StringLengthInRange{Name: \"CredentialId\", Field: credential.CredentialId, Min: 1, Max: 0},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: credential.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: credential.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/webhook.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gofrs/uuid\"\n)\n\n// Webhook is used by pop to map your webhooks database table to your go code.\ntype Webhook struct {\n\tID            uuid.UUID     `json:\"id\" db:\"id\"`\n\tCallback      string        `json:\"callback\" db:\"callback\"`\n\tEnabled       bool          `json:\"enabled\" db:\"enabled\"`\n\tFailures      int           `json:\"failures\" db:\"failures\"`\n\tExpiresAt     time.Time     `json:\"expires_at\" db:\"expires_at\"`\n\tWebhookEvents WebhookEvents `json:\"events\" has_many:\"webhook_events\"`\n\tCreatedAt     time.Time     `json:\"created_at\" db:\"created_at\"`\n\tUpdatedAt     time.Time     `json:\"updated_at\" db:\"updated_at\"`\n}\n\n// Webhooks are not required by pop and may be deleted\ntype Webhooks []Webhook\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\n// This method is not required and may be deleted.\nfunc (w *Webhook) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: w.ID},\n\t\t&validators.StringIsPresent{Name: \"Callback\", Field: w.Callback},\n\t\t&validators.TimeIsPresent{Name: \"ExpiresAt\", Field: w.ExpiresAt},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: w.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: w.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/models/webhook_event.go",
    "content": "package models\n\nimport (\n\t\"github.com/gobuffalo/validate/v3/validators\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/gofrs/uuid\"\n)\n\n// WebhookEvent is used by pop to map your webhook_events database table to your go code.\ntype WebhookEvent struct {\n\tID        uuid.UUID `json:\"id\" db:\"id\"`\n\tWebhook   *Webhook  `json:\"-\" belongs_to:\"webhook\"`\n\tWebhookID uuid.UUID `json:\"-\" db:\"webhook_id\"`\n\tEvent     string    `json:\"event\" db:\"event\"`\n\tCreatedAt time.Time `json:\"-\" db:\"created_at\"`\n\tUpdatedAt time.Time `json:\"-\" db:\"updated_at\"`\n}\n\n// WebhookEvents is not required by pop and may be deleted\ntype WebhookEvents []WebhookEvent\n\n// Validate gets run every time you call a \"pop.Validate*\" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.\n// This method is not required and may be deleted.\nfunc (w *WebhookEvent) Validate(tx *pop.Connection) (*validate.Errors, error) {\n\treturn validate.Validate(\n\t\t&validators.UUIDIsPresent{Name: \"ID\", Field: w.ID},\n\t\t&validators.StringIsPresent{Name: \"Event\", Field: w.Event},\n\t\t&validators.TimeIsPresent{Name: \"UpdatedAt\", Field: w.UpdatedAt},\n\t\t&validators.TimeIsPresent{Name: \"CreatedAt\", Field: w.CreatedAt},\n\t), nil\n}\n"
  },
  {
    "path": "backend/persistence/otp_secret_persister.go",
    "content": "package persistence\n\nimport (\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype OTPSecretPersister interface {\n\tCreate(models.OTPSecret) error\n\tUpdate(*models.OTPSecret) error\n\tDelete(*models.OTPSecret) error\n}\n\ntype otpSecretPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewOTPSecretPersister(db *pop.Connection) OTPSecretPersister {\n\treturn &otpSecretPersister{db: db}\n}\n\nfunc (p *otpSecretPersister) Create(secret models.OTPSecret) error {\n\tvErr, err := p.db.ValidateAndCreate(&secret)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store otp secret credential: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"otp secret object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *otpSecretPersister) Update(secret *models.OTPSecret) error {\n\tvErr, err := p.db.ValidateAndUpdate(secret)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update otp secret: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"otp secret object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *otpSecretPersister) Delete(secret *models.OTPSecret) error {\n\terr := p.db.Destroy(secret)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete otp secret: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/passcode_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype PasscodePersister interface {\n\tGet(uuid.UUID) (*models.Passcode, error)\n\tCreate(models.Passcode) error\n\tUpdate(models.Passcode) error\n\tDelete(models.Passcode) error\n}\n\ntype passcodePersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewPasscodePersister(db *pop.Connection) PasscodePersister {\n\treturn &passcodePersister{db: db}\n}\n\nfunc (p *passcodePersister) Get(id uuid.UUID) (*models.Passcode, error) {\n\tpasscode := models.Passcode{}\n\terr := p.db.EagerPreload(\"Email.User\").Find(&passcode, id)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get passcode: %w\", err)\n\t}\n\n\treturn &passcode, nil\n}\n\nfunc (p *passcodePersister) Create(passcode models.Passcode) error {\n\tvErr, err := p.db.ValidateAndCreate(&passcode)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store passcode: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"passcode object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *passcodePersister) Update(passcode models.Passcode) error {\n\tvErr, err := p.db.ValidateAndUpdate(&passcode)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update passcode: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"passcode object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *passcodePersister) Delete(passcode models.Passcode) error {\n\terr := p.db.Destroy(&passcode)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete passcode: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/password_credential_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype PasswordCredentialPersister interface {\n\tCreate(password models.PasswordCredential) error\n\tGetByUserID(userId uuid.UUID) (*models.PasswordCredential, error)\n\tUpdate(password models.PasswordCredential) error\n\tDelete(password models.PasswordCredential) error\n}\n\ntype passwordCredentialPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewPasswordCredentialPersister(db *pop.Connection) PasswordCredentialPersister {\n\treturn &passwordCredentialPersister{db: db}\n}\n\nfunc (p *passwordCredentialPersister) Create(password models.PasswordCredential) error {\n\tvErr, err := p.db.ValidateAndCreate(&password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store password credential: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"password object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *passwordCredentialPersister) GetByUserID(userId uuid.UUID) (*models.PasswordCredential, error) {\n\tpw := models.PasswordCredential{}\n\tquery := p.db.Where(\"user_id = (?)\", userId.String())\n\terr := query.First(&pw)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get credential: %w\", err)\n\t}\n\treturn &pw, nil\n}\n\nfunc (p *passwordCredentialPersister) Update(password models.PasswordCredential) error {\n\tvErr, err := p.db.ValidateAndUpdate(&password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update password: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"password object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *passwordCredentialPersister) Delete(password models.PasswordCredential) error {\n\terr := p.db.Destroy(&password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete user: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/persister.go",
    "content": "package persistence\n\nimport (\n\t\"embed\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n)\n\n//go:embed migrations/*\nvar migrations embed.FS\n\n// Persister is the persistence interface connecting to the database and capable of doing migrations\ntype persister struct {\n\tDB *pop.Connection\n}\n\ntype Persister interface {\n\tGetAuditLogPersister() AuditLogPersister\n\tGetAuditLogPersisterWithConnection(tx *pop.Connection) AuditLogPersister\n\tGetConnection() *pop.Connection\n\tGetFlowPersister() FlowPersister\n\tGetFlowPersisterWithConnection(tx *pop.Connection) FlowPersister\n\tGetEmailPersister() EmailPersister\n\tGetEmailPersisterWithConnection(tx *pop.Connection) EmailPersister\n\tGetIdentityPersister() IdentityPersister\n\tGetIdentityPersisterWithConnection(tx *pop.Connection) IdentityPersister\n\tGetJwkPersister() JwkPersister\n\tGetJwkPersisterWithConnection(tx *pop.Connection) JwkPersister\n\tGetPasscodePersister() PasscodePersister\n\tGetPasscodePersisterWithConnection(tx *pop.Connection) PasscodePersister\n\tGetPasswordCredentialPersister() PasswordCredentialPersister\n\tGetPasswordCredentialPersisterWithConnection(tx *pop.Connection) PasswordCredentialPersister\n\tGetPrimaryEmailPersister() PrimaryEmailPersister\n\tGetPrimaryEmailPersisterWithConnection(tx *pop.Connection) PrimaryEmailPersister\n\tGetSamlCertificatePersister() SamlCertificatePersister\n\tGetSamlCertificatePersisterWithConnection(tx *pop.Connection) SamlCertificatePersister\n\tGetSamlStatePersister() SamlStatePersister\n\tGetSamlStatePersisterWithConnection(tx *pop.Connection) SamlStatePersister\n\tGetSamlIdentityPersister() SamlIdentityPersister\n\tGetSamlIdentityPersisterWithConnection(tx *pop.Connection) SamlIdentityPersister\n\tGetSamlIDPInitiatedRequestPersister() SamlIDPInitiatedRequestPersister\n\tGetSamlIDPInitiatedRequestPersisterWithConnection(tx *pop.Connection) SamlIDPInitiatedRequestPersister\n\tGetTokenPersister() TokenPersister\n\tGetTokenPersisterWithConnection(tx *pop.Connection) TokenPersister\n\tGetUserPersister() UserPersister\n\tGetUserPersisterWithConnection(tx *pop.Connection) UserPersister\n\tGetUserMetadataPersister() UserMetadataPersister\n\tGetUserMetadataPersisterWithConnection(tx *pop.Connection) UserMetadataPersister\n\tGetWebauthnCredentialPersister() WebauthnCredentialPersister\n\tGetWebauthnCredentialPersisterWithConnection(tx *pop.Connection) WebauthnCredentialPersister\n\tGetWebauthnSessionDataPersister() WebauthnSessionDataPersister\n\tGetWebauthnSessionDataPersisterWithConnection(tx *pop.Connection) WebauthnSessionDataPersister\n\tGetWebhookPersister(tx *pop.Connection) WebhookPersister\n\tGetTrustedDevicePersister() TrustedDevicePersister\n\tGetTrustedDevicePersisterWithConnection(tx *pop.Connection) TrustedDevicePersister\n\tGetUsernamePersister() UsernamePersister\n\tGetUsernamePersisterWithConnection(tx *pop.Connection) UsernamePersister\n\tGetSessionPersister() SessionPersister\n\tGetSessionPersisterWithConnection(tx *pop.Connection) SessionPersister\n\tGetOTPSecretPersister() OTPSecretPersister\n\tGetOTPSecretPersisterWithConnection(tx *pop.Connection) OTPSecretPersister\n\tGetWebauthnCredentialUserHandlePersister() WebauthnCredentialUserHandlePersister\n\tGetWebauthnCredentialUserHandlePersisterWithConnection(tx *pop.Connection) WebauthnCredentialUserHandlePersister\n\tTransaction(func(tx *pop.Connection) error) error\n}\n\ntype Cleanup[T any] interface {\n\tFindExpired(cutoffTime time.Time, page, perPage int) ([]T, error)\n\tDelete(item T) error\n}\n\ntype Migrator interface {\n\tMigrateUp() error\n\tMigrateDown(int) error\n}\n\ntype Storage interface {\n\tMigrator\n\tPersister\n}\n\n// New return a new Persister Object with given configuration\nfunc New(config config.Database) (Storage, error) {\n\tconnectionDetails := &pop.ConnectionDetails{\n\t\tPool:            5,\n\t\tIdlePool:        0,\n\t\tConnMaxIdleTime: 5 * time.Minute,\n\t\tConnMaxLifetime: 1 * time.Hour,\n\t}\n\tif len(config.Url) > 0 {\n\t\tconnectionDetails.URL = config.Url\n\t} else {\n\t\tconnectionDetails.Dialect = config.Dialect\n\t\tconnectionDetails.Database = config.Database\n\t\tconnectionDetails.Host = config.Host\n\t\tconnectionDetails.Port = config.Port\n\t\tconnectionDetails.User = config.User\n\t\tconnectionDetails.Password = config.Password\n\t}\n\n\tDB, err := pop.NewConnection(connectionDetails)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := DB.Open(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &persister{\n\t\tDB: DB,\n\t}, nil\n}\n\n// MigrateUp applies all pending up migrations to the Database\nfunc (p *persister) MigrateUp() error {\n\tmigrationBox, err := pop.NewMigrationBox(migrations, p.DB)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = migrationBox.Up()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// MigrateDown migrates the Database down by the given number of steps\nfunc (p *persister) MigrateDown(steps int) error {\n\tmigrationBox, err := pop.NewMigrationBox(migrations, p.DB)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = migrationBox.Down(steps)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (p *persister) GetConnection() *pop.Connection {\n\treturn p.DB\n}\n\nfunc (p *persister) GetFlowPersister() FlowPersister {\n\treturn NewFlowPersister(p.DB)\n}\n\nfunc (p *persister) GetFlowPersisterWithConnection(tx *pop.Connection) FlowPersister {\n\treturn NewFlowPersister(tx)\n}\n\nfunc (p *persister) GetIdentityPersister() IdentityPersister {\n\treturn NewIdentityPersister(p.DB)\n}\n\nfunc (p *persister) GetIdentityPersisterWithConnection(tx *pop.Connection) IdentityPersister {\n\treturn NewIdentityPersister(tx)\n}\n\nfunc (p *persister) GetUserPersister() UserPersister {\n\treturn NewUserPersister(p.DB)\n}\n\nfunc (p *persister) GetUserPersisterWithConnection(tx *pop.Connection) UserPersister {\n\treturn NewUserPersister(tx)\n}\n\nfunc (p *persister) GetPasscodePersister() PasscodePersister {\n\treturn NewPasscodePersister(p.DB)\n}\n\nfunc (p *persister) GetPasscodePersisterWithConnection(tx *pop.Connection) PasscodePersister {\n\treturn NewPasscodePersister(tx)\n}\n\nfunc (p *persister) GetPasswordCredentialPersister() PasswordCredentialPersister {\n\treturn NewPasswordCredentialPersister(p.DB)\n}\n\nfunc (p *persister) GetPasswordCredentialPersisterWithConnection(tx *pop.Connection) PasswordCredentialPersister {\n\treturn NewPasswordCredentialPersister(tx)\n}\n\nfunc (p *persister) GetTrustedDevicePersister() TrustedDevicePersister {\n\treturn NewTrustedDevicePersister(p.DB)\n}\n\nfunc (p *persister) GetTrustedDevicePersisterWithConnection(tx *pop.Connection) TrustedDevicePersister {\n\treturn NewTrustedDevicePersister(tx)\n}\n\nfunc (p *persister) GetUsernamePersister() UsernamePersister {\n\treturn NewUsernamePersister(p.DB)\n}\n\nfunc (p *persister) GetUsernamePersisterWithConnection(tx *pop.Connection) UsernamePersister {\n\treturn NewUsernamePersister(tx)\n}\n\nfunc (p *persister) GetOTPSecretPersister() OTPSecretPersister {\n\treturn NewOTPSecretPersister(p.DB)\n}\n\nfunc (p *persister) GetOTPSecretPersisterWithConnection(tx *pop.Connection) OTPSecretPersister {\n\treturn NewOTPSecretPersister(tx)\n}\n\nfunc (p *persister) GetWebauthnCredentialPersister() WebauthnCredentialPersister {\n\treturn NewWebauthnCredentialPersister(p.DB)\n}\n\nfunc (p *persister) GetWebauthnCredentialPersisterWithConnection(tx *pop.Connection) WebauthnCredentialPersister {\n\treturn NewWebauthnCredentialPersister(tx)\n}\n\nfunc (p *persister) GetWebauthnSessionDataPersister() WebauthnSessionDataPersister {\n\treturn NewWebauthnSessionDataPersister(p.DB)\n}\n\nfunc (p *persister) GetWebauthnSessionDataPersisterWithConnection(tx *pop.Connection) WebauthnSessionDataPersister {\n\treturn NewWebauthnSessionDataPersister(tx)\n}\n\nfunc (p *persister) GetJwkPersister() JwkPersister {\n\treturn NewJwkPersister(p.DB)\n}\n\nfunc (p *persister) GetJwkPersisterWithConnection(tx *pop.Connection) JwkPersister {\n\treturn NewJwkPersister(tx)\n}\n\nfunc (p *persister) GetAuditLogPersister() AuditLogPersister {\n\treturn NewAuditLogPersister(p.DB)\n}\n\nfunc (p *persister) GetAuditLogPersisterWithConnection(tx *pop.Connection) AuditLogPersister {\n\treturn NewAuditLogPersister(tx)\n}\n\nfunc (p *persister) GetEmailPersister() EmailPersister {\n\treturn NewEmailPersister(p.DB)\n}\n\nfunc (p *persister) GetEmailPersisterWithConnection(tx *pop.Connection) EmailPersister {\n\treturn NewEmailPersister(tx)\n}\n\nfunc (p *persister) GetPrimaryEmailPersister() PrimaryEmailPersister {\n\treturn NewPrimaryEmailPersister(p.DB)\n}\n\nfunc (p *persister) GetPrimaryEmailPersisterWithConnection(tx *pop.Connection) PrimaryEmailPersister {\n\treturn NewPrimaryEmailPersister(tx)\n}\n\nfunc (p *persister) Transaction(fn func(tx *pop.Connection) error) error {\n\treturn p.DB.Transaction(fn)\n}\n\nfunc (p *persister) GetTokenPersister() TokenPersister {\n\treturn NewTokenPersister(p.DB)\n}\n\nfunc (p *persister) GetTokenPersisterWithConnection(tx *pop.Connection) TokenPersister {\n\treturn NewTokenPersister(tx)\n}\n\nfunc (p *persister) GetSamlStatePersister() SamlStatePersister {\n\treturn NewSamlStatePersister(p.DB)\n}\n\nfunc (p *persister) GetSamlStatePersisterWithConnection(tx *pop.Connection) SamlStatePersister {\n\treturn NewSamlStatePersister(tx)\n}\n\nfunc (p *persister) GetSamlCertificatePersister() SamlCertificatePersister {\n\treturn NewSamlCertificatePersister(p.DB)\n}\n\nfunc (p *persister) GetSamlCertificatePersisterWithConnection(tx *pop.Connection) SamlCertificatePersister {\n\treturn NewSamlCertificatePersister(tx)\n}\n\nfunc (p *persister) GetSamlIdentityPersister() SamlIdentityPersister {\n\treturn NewSamlIdentityPersister(p.DB)\n}\n\nfunc (p *persister) GetSamlIdentityPersisterWithConnection(tx *pop.Connection) SamlIdentityPersister {\n\treturn NewSamlIdentityPersister(tx)\n}\n\nfunc (p *persister) GetSamlIDPInitiatedRequestPersister() SamlIDPInitiatedRequestPersister {\n\treturn NewSamlIDPInitiatedRequestPersister(p.DB)\n}\n\nfunc (p *persister) GetSamlIDPInitiatedRequestPersisterWithConnection(tx *pop.Connection) SamlIDPInitiatedRequestPersister {\n\treturn NewSamlIDPInitiatedRequestPersister(tx)\n}\n\nfunc (p *persister) GetWebhookPersister(tx *pop.Connection) WebhookPersister {\n\tif tx != nil {\n\t\treturn NewWebhookPersister(tx)\n\t}\n\n\treturn NewWebhookPersister(p.DB)\n}\n\nfunc (p *persister) GetSessionPersister() SessionPersister {\n\treturn NewSessionPersister(p.DB)\n}\n\nfunc (p *persister) GetSessionPersisterWithConnection(tx *pop.Connection) SessionPersister {\n\treturn NewSessionPersister(tx)\n}\n\nfunc (p *persister) GetWebauthnCredentialUserHandlePersister() WebauthnCredentialUserHandlePersister {\n\treturn NewWebauthnCredentialUserHandlePersister(p.DB)\n}\n\nfunc (p *persister) GetWebauthnCredentialUserHandlePersisterWithConnection(tx *pop.Connection) WebauthnCredentialUserHandlePersister {\n\treturn NewWebauthnCredentialUserHandlePersister(tx)\n}\n\nfunc (p *persister) GetUserMetadataPersister() UserMetadataPersister {\n\treturn NewUserMetadataPersister(p.DB)\n}\n\nfunc (p *persister) GetUserMetadataPersisterWithConnection(tx *pop.Connection) UserMetadataPersister {\n\treturn NewUserMetadataPersister(tx)\n}\n"
  },
  {
    "path": "backend/persistence/primary_email_persister.go",
    "content": "package persistence\n\nimport (\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype PrimaryEmailPersister interface {\n\tCreate(models.PrimaryEmail) error\n\tUpdate(models.PrimaryEmail) error\n\tDelete(models.PrimaryEmail) error\n}\n\ntype primaryEmailPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewPrimaryEmailPersister(db *pop.Connection) PrimaryEmailPersister {\n\treturn &primaryEmailPersister{db: db}\n}\n\nfunc (p *primaryEmailPersister) Create(primaryEmail models.PrimaryEmail) error {\n\tvErr, err := p.db.ValidateAndCreate(&primaryEmail)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"primary email object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *primaryEmailPersister) Update(primaryEmail models.PrimaryEmail) error {\n\tvErr, err := p.db.ValidateAndSave(&primaryEmail)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"primary email object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (e *primaryEmailPersister) Delete(primaryEmail models.PrimaryEmail) error {\n\terr := e.db.Destroy(&primaryEmail)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete email: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/saml_certificate_persister.go",
    "content": "package persistence\n\nimport (\n\t\"crypto/x509\"\n\t\"database/sql\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SamlCertificatePersister interface {\n\tCreate(cert *models.SamlCertificate) error\n\tGetFirst() (*models.SamlCertificate, error)\n\tRenew(cert *models.SamlCertificate, serviceName string) error\n\tDelete(cert *models.SamlCertificate) error\n}\n\ntype samlCertificatePersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewSamlCertificatePersister(db *pop.Connection) SamlCertificatePersister {\n\treturn &samlCertificatePersister{db: db}\n}\n\nfunc (s samlCertificatePersister) GetFirst() (*models.SamlCertificate, error) {\n\tcert := models.SamlCertificate{}\n\n\terr := s.db.First(&cert)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get certificate: %w\", err)\n\t}\n\n\treturn &cert, nil\n}\n\nfunc (s samlCertificatePersister) Create(cert *models.SamlCertificate) error {\n\tvalidationError, err := s.db.ValidateAndCreate(cert)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif validationError != nil && validationError.HasAny() {\n\t\treturn fmt.Errorf(\"saml certificate validation failed: %w\", validationError)\n\t}\n\n\treturn nil\n}\n\nfunc (s samlCertificatePersister) Renew(cert *models.SamlCertificate, serviceName string) error {\n\tkey, err := cert.DecryptCertKey()\n\tif key == nil || err != nil {\n\t\treturn fmt.Errorf(\"unable to decrypt private key: %w\", err)\n\t}\n\n\tdecodedKey, _ := pem.Decode(key)\n\tif decodedKey == nil || decodedKey.Type != \"RSA PRIVATE KEY\" {\n\t\treturn fmt.Errorf(\"unable to decode private key\")\n\t}\n\n\tparsedKey, err := x509.ParsePKCS1PrivateKey(decodedKey.Bytes)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to parse private key: %w\", err)\n\t}\n\n\tnow := time.Now()\n\n\tnewCert, err := models.GenerateCertificate(serviceName, parsedKey, now)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to renew certificate: %w\", err)\n\t}\n\n\tcert.CertData = newCert\n\n\tvalidationError, err := s.db.ValidateAndUpdate(s)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update certificate: %w\", err)\n\t}\n\n\tif validationError != nil && validationError.HasAny() {\n\t\treturn fmt.Errorf(\"saml certificate validation failed: %v\", validationError)\n\t}\n\n\treturn nil\n}\n\nfunc (s samlCertificatePersister) Delete(cert *models.SamlCertificate) error {\n\terr := s.db.Destroy(cert)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete certificate: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/saml_identity_persister.go",
    "content": "package persistence\n\nimport (\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SamlIdentityPersister interface {\n\tCreate(samlIdentity models.SamlIdentity) error\n\tUpdate(samlIdentity models.SamlIdentity) error\n}\n\ntype samlIdentityPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewSamlIdentityPersister(db *pop.Connection) SamlIdentityPersister {\n\treturn &samlIdentityPersister{db: db}\n}\n\nfunc (p samlIdentityPersister) Create(samlIdentity models.SamlIdentity) error {\n\tvErr, err := p.db.Eager().ValidateAndCreate(&samlIdentity)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store saml identity: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"saml identity object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p samlIdentityPersister) Update(samlIdentity models.SamlIdentity) error {\n\tvErr, err := p.db.ValidateAndUpdate(&samlIdentity)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update saml identity: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"saml identity object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/saml_idp_inititated_request_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SamlIDPInitiatedRequestPersister interface {\n\tCreate(samlIDPInitiatedRequest models.SamlIDPInitiatedRequest) error\n\tGetByResponseIDAndIssuer(responseID, entityID string) (*models.SamlIDPInitiatedRequest, error)\n}\n\ntype samlIDPInitiatedRequestPersister struct {\n\tdb *pop.Connection\n}\n\nfunc (p samlIDPInitiatedRequestPersister) GetByResponseIDAndIssuer(responseID, entityID string) (*models.SamlIDPInitiatedRequest, error) {\n\tsamlIDPInitiatedRequest := models.SamlIDPInitiatedRequest{}\n\tquery := p.db.Where(\"response_id = ? AND idp_entity_id = ?\", responseID, entityID)\n\terr := query.First(&samlIDPInitiatedRequest)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get credential: %w\", err)\n\t}\n\treturn &samlIDPInitiatedRequest, nil\n}\n\nfunc NewSamlIDPInitiatedRequestPersister(db *pop.Connection) SamlIDPInitiatedRequestPersister {\n\treturn &samlIDPInitiatedRequestPersister{db: db}\n}\n\nfunc (p samlIDPInitiatedRequestPersister) Create(samlIDPInitiatedRequest models.SamlIDPInitiatedRequest) error {\n\tvErr, err := p.db.ValidateAndCreate(&samlIDPInitiatedRequest)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store saml idp initiated request: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"saml idp initated request object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/saml_state_persister.go",
    "content": "package persistence\n\nimport (\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype SamlStatePersister interface {\n\tCreate(state models.SamlState) error\n\tGetByNonce(nonce string) (*models.SamlState, error)\n\tDelete(state models.SamlState) error\n}\n\ntype samlStatePersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewSamlStatePersister(db *pop.Connection) SamlStatePersister {\n\treturn &samlStatePersister{db: db}\n}\n\nfunc (s samlStatePersister) Create(state models.SamlState) error {\n\tvalidationError, err := s.db.ValidateAndCreate(&state)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif validationError != nil && validationError.HasAny() {\n\t\treturn fmt.Errorf(\"token object validation failed: %w\", validationError)\n\t}\n\n\treturn nil\n}\n\nfunc (s samlStatePersister) GetByNonce(nonce string) (*models.SamlState, error) {\n\tstate := models.SamlState{}\n\n\terr := s.db.Where(\"nonce = ?\", nonce).First(&state)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get state by nonce: %w\", err)\n\t}\n\n\treturn &state, nil\n}\n\nfunc (s samlStatePersister) Delete(state models.SamlState) error {\n\terr := s.db.Destroy(&state)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete state: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/session_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\ntype SessionPersister interface {\n\tCreate(session models.Session) error\n\tGet(id uuid.UUID) (*models.Session, error)\n\tUpdate(session models.Session) error\n\tList(userID uuid.UUID) ([]models.Session, error)\n\tListActive(userID uuid.UUID) ([]models.Session, error)\n\tDelete(session models.Session) error\n}\n\ntype sessionPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewSessionPersister(db *pop.Connection) SessionPersister {\n\treturn &sessionPersister{db: db}\n}\n\nfunc (p *sessionPersister) Create(session models.Session) error {\n\tvErr, err := p.db.ValidateAndCreate(&session)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store session: %w\", err)\n\t}\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"session object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *sessionPersister) Get(id uuid.UUID) (*models.Session, error) {\n\tsession := models.Session{}\n\terr := p.db.Eager().Find(&session, id)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get session: %w\", err)\n\t}\n\n\treturn &session, nil\n}\n\nfunc (p *sessionPersister) Update(session models.Session) error {\n\tvErr, err := p.db.ValidateAndUpdate(&session)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"session object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *sessionPersister) List(userID uuid.UUID) ([]models.Session, error) {\n\tsessions := []models.Session{}\n\n\terr := p.db.Q().Where(\"user_id = ?\", userID).All(&sessions)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn sessions, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fetch sessions: %w\", err)\n\t}\n\n\treturn sessions, nil\n}\n\nfunc (p *sessionPersister) ListActive(userID uuid.UUID) ([]models.Session, error) {\n\tsessions := []models.Session{}\n\n\terr := p.db.Q().Where(\"user_id = ?\", userID).Where(\"expires_at > ?\", time.Now()).Order(\"created_at desc\").All(&sessions)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn sessions, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fetch sessions: %w\", err)\n\t}\n\n\treturn sessions, nil\n}\n\nfunc (p *sessionPersister) Delete(session models.Session) error {\n\terr := p.db.Eager().Destroy(&session)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete session: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/token_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype TokenPersister interface {\n\tCreate(token models.Token) error\n\tGetByValue(value string) (*models.Token, error)\n\tDelete(token models.Token) error\n}\n\ntype tokenPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewTokenPersister(db *pop.Connection) TokenPersister {\n\treturn &tokenPersister{db: db}\n}\n\nfunc (t tokenPersister) Create(token models.Token) error {\n\tvErr, err := t.db.ValidateAndCreate(&token)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"token object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (t tokenPersister) GetByValue(value string) (*models.Token, error) {\n\ttoken := models.Token{}\n\terr := t.db.Where(\"value = ?\", value).First(&token)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get token by value: %w\", err)\n\t}\n\n\treturn &token, nil\n}\n\nfunc (t tokenPersister) Delete(token models.Token) error {\n\terr := t.db.Destroy(&token)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete token: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/trusted_device_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype TrustedDevicePersister interface {\n\tCreate(models.TrustedDevice) error\n\tFindByDeviceToken(string) (*models.TrustedDevice, error)\n}\n\ntype trustedDevicePersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewTrustedDevicePersister(db *pop.Connection) TrustedDevicePersister {\n\treturn &trustedDevicePersister{db: db}\n}\n\nfunc (p *trustedDevicePersister) Create(trustedDevice models.TrustedDevice) error {\n\tvErr, err := p.db.ValidateAndCreate(&trustedDevice)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store trustedDevice: %w\", err)\n\t}\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"trustedDevice object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *trustedDevicePersister) FindByDeviceToken(token string) (*models.TrustedDevice, error) {\n\ttrustedDevice := models.TrustedDevice{}\n\terr := p.db.Where(\"device_token = ?\", token).First(&trustedDevice)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get trustedDevice: %w\", err)\n\t}\n\n\treturn &trustedDevice, nil\n}\n"
  },
  {
    "path": "backend/persistence/user_metadata_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"strings\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/validate/v3\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\n// MetadataLimitExceededError is returned when metadata fields exceed their length limits\ntype MetadataLimitExceededError struct {\n\tValidationErrors *validate.Errors\n}\n\nfunc (e *MetadataLimitExceededError) Error() string {\n\treturn fmt.Sprintf(\n\t\t\"metadata limit exceeded: %s\",\n\t\tstrings.Replace(e.ValidationErrors.Error(), \"\\n\", \", \", -1))\n}\n\nfunc (e *MetadataLimitExceededError) Unwrap() error {\n\treturn e.ValidationErrors\n}\n\n// IsMetadataLimitExceededError checks if the error is a MetadataLimitExceededError\nfunc IsMetadataLimitExceededError(err error) bool {\n\tvar metadataLimitExceededError *MetadataLimitExceededError\n\tok := errors.As(err, &metadataLimitExceededError)\n\treturn ok\n}\n\ntype UserMetadataPersister interface {\n\tGet(userID uuid.UUID) (*models.UserMetadata, error)\n\tUpdate(metadata *models.UserMetadata) error\n\tDelete(metadata *models.UserMetadata) error\n}\n\ntype userMetadataPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewUserMetadataPersister(db *pop.Connection) UserMetadataPersister {\n\treturn &userMetadataPersister{db: db}\n}\n\nfunc (p *userMetadataPersister) Get(userID uuid.UUID) (*models.UserMetadata, error) {\n\tmetadata := &models.UserMetadata{}\n\terr := p.db.Where(\"user_id = ?\", userID).First(metadata)\n\tif err != nil {\n\t\tif errors.Is(err, sql.ErrNoRows) {\n\t\t\t// Create new metadata if none exists\n\t\t\tmetadata = &models.UserMetadata{UserID: userID}\n\t\t\terr = p.db.Create(metadata)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn metadata, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn metadata, nil\n}\n\nfunc (p *userMetadataPersister) Update(metadata *models.UserMetadata) error {\n\tvErr, err := p.db.ValidateAndUpdate(metadata)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update metadata: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\tmetadataRangeErrors := validate.NewErrors()\n\t\tfor key, errs := range vErr.Errors {\n\t\t\tif key == \"public\" || key == \"private\" || key == \"unsafe\" {\n\t\t\t\tfor _, errMsg := range errs {\n\t\t\t\t\tif strings.Contains(errMsg, \"metadata must not exceed\") {\n\t\t\t\t\t\tmetadataRangeErrors.Add(key, errMsg)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif metadataRangeErrors.HasAny() {\n\t\t\treturn &MetadataLimitExceededError{ValidationErrors: metadataRangeErrors}\n\t\t}\n\t\treturn fmt.Errorf(\"metadata validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *userMetadataPersister) Delete(metadata *models.UserMetadata) error {\n\terr := p.db.Destroy(metadata)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete user metadata: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/user_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype UserPersister interface {\n\tGet(uuid.UUID) (*models.User, error)\n\tGetByEmailAddress(string) (*models.User, error)\n\tCreate(models.User) error\n\tUpdate(models.User) error\n\tDelete(models.User) error\n\tList(page int, perPage int, userIDs []uuid.UUID, email string, username string, sortDirection string) ([]models.User, error)\n\tAll() ([]models.User, error)\n\tCount(userIDs []uuid.UUID, email string, username string) (int, error)\n\tGetByUsername(username string) (*models.User, error)\n}\n\ntype userPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewUserPersister(db *pop.Connection) UserPersister {\n\treturn &userPersister{db: db}\n}\n\nfunc (p *userPersister) Get(id uuid.UUID) (*models.User, error) {\n\tuser := models.User{}\n\n\teagerPreloadFields := []string{\n\t\t\"Emails\",\n\t\t\"Emails.PrimaryEmail\",\n\t\t\"Emails.Identities.SamlIdentity\",\n\t\t\"WebauthnCredentials\",\n\t\t\"WebauthnCredentials.Transports\",\n\t\t\"Username\",\n\t\t\"PasswordCredential\",\n\t\t\"OTPSecret\",\n\t\t\"Metadata\",\n\t\t\"Identities\",\n\t\t\"Identities.SamlIdentity\",\n\t}\n\n\terr := p.db.EagerPreload(eagerPreloadFields...).Find(&user, id)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\treturn &user, nil\n}\n\nfunc (p *userPersister) GetByEmailAddress(emailAddress string) (*models.User, error) {\n\temail := models.Email{}\n\terr := p.db.Eager().Where(\"address = (?)\", emailAddress).First(&email)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get user by email address: %w\", err)\n\t}\n\n\tif email.UserID == nil {\n\t\treturn nil, nil\n\t}\n\n\treturn p.Get(*email.UserID)\n}\n\nfunc (p *userPersister) GetByUsername(username string) (*models.User, error) {\n\tuser := models.User{}\n\terr := p.db.EagerPreload(\n\t\t\"Emails\",\n\t\t\"Emails.PrimaryEmail\",\n\t\t\"Emails.Identities\",\n\t\t\"WebauthnCredentials\",\n\t\t\"PasswordCredential\",\n\t\t\"Username\",\n\t\t\"OTPSecret\",\n\t\t\"Metadata\").\n\t\tLeftJoin(\"usernames\", \"usernames.user_id = users.id\").\n\t\tWhere(\"usernames.username = (?)\", username).\n\t\tFirst(&user)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get user: %w\", err)\n\t}\n\n\treturn &user, nil\n}\n\nfunc (p *userPersister) Create(user models.User) error {\n\tvErr, err := p.db.ValidateAndCreate(&user)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store user: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"user object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *userPersister) Update(user models.User) error {\n\tvErr, err := p.db.ValidateAndUpdate(&user)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update user: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"user object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\n// Delete deletes a user from the database including all information (e.g., emails, username, metadata)\n// It must be called within a transaction otherwise some information might not be rolled back on an error.\nfunc (p *userPersister) Delete(user models.User) error {\n\tprimaryEmail := user.Emails.GetPrimary()\n\n\tif primaryEmail != nil {\n\t\terr := p.db.Destroy(primaryEmail.PrimaryEmail)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to delete primary email: %w\", err)\n\t\t}\n\t}\n\n\terr := p.db.Destroy(&user)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete user: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (p *userPersister) List(page int, perPage int, userIDs []uuid.UUID, email string, username string, sortDirection string) ([]models.User, error) {\n\tusers := []models.User{}\n\n\tquery := p.db.\n\t\tQ().\n\t\tEagerPreload(\n\t\t\t\"Emails\",\n\t\t\t\"Emails.PrimaryEmail\",\n\t\t\t\"WebauthnCredentials\",\n\t\t\t\"WebauthnCredentials.Transports\",\n\t\t\t\"Username\").\n\t\tLeftJoin(\"emails\", \"emails.user_id = users.id\").\n\t\tLeftJoin(\"usernames\", \"usernames.user_id = users.id\")\n\tquery = p.addQueryParamsToSqlQuery(query, userIDs, email, username)\n\terr := query.GroupBy(\"users.id\").\n\t\tHaving(\"count(emails.id) > 0 OR count(usernames.id) > 0\").\n\t\tOrder(fmt.Sprintf(\"users.created_at %s\", sortDirection)).\n\t\tPaginate(page, perPage).\n\t\tAll(&users)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn users, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fetch users: %w\", err)\n\t}\n\n\treturn users, nil\n}\n\nfunc (p *userPersister) All() ([]models.User, error) {\n\tusers := []models.User{}\n\n\terr := p.db.EagerPreload(\n\t\t\"Emails\",\n\t\t\"Emails.PrimaryEmail\",\n\t\t\"Emails.Identities\",\n\t\t\"WebauthnCredentials\",\n\t\t\"WebauthnCredentials.Transports\",\n\t\t\"Username\",\n\t).All(&users)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn users, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fetch users: %w\", err)\n\t}\n\n\treturn users, nil\n}\n\nfunc (p *userPersister) Count(userIDs []uuid.UUID, email string, username string) (int, error) {\n\tquery := p.db.\n\t\tQ().\n\t\tLeftJoin(\"emails\", \"emails.user_id = users.id\").\n\t\tLeftJoin(\"usernames\", \"usernames.user_id = users.id\")\n\tquery = p.addQueryParamsToSqlQuery(query, userIDs, email, username)\n\tcount, err := query.GroupBy(\"users.id\").\n\t\tHaving(\"count(emails.id) > 0 OR count(usernames.id) > 0\").\n\t\tCount(&models.User{})\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to get user count: %w\", err)\n\t}\n\n\treturn count, nil\n}\n\nfunc (p *userPersister) addQueryParamsToSqlQuery(query *pop.Query, userIDs []uuid.UUID, email string, username string) *pop.Query {\n\tif email != \"\" && username != \"\" {\n\t\tquery = query.Where(\"emails.address LIKE ? OR usernames.username LIKE ?\", \"%\"+email+\"%\", \"%\"+username+\"%\")\n\t} else if email != \"\" {\n\t\tquery = query.Where(\"emails.address LIKE ?\", \"%\"+email+\"%\")\n\t} else if username != \"\" {\n\t\tquery = query.Where(\"usernames.username LIKE ?\", \"%\"+username+\"%\")\n\t}\n\n\tif len(userIDs) > 0 {\n\t\tquery = query.Where(\"users.id in (?)\", userIDs)\n\t}\n\n\treturn query\n}\n"
  },
  {
    "path": "backend/persistence/username_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype UsernamePersister interface {\n\tCreate(username models.Username) error\n\tGetByName(name string) (*models.Username, error)\n\tUpdate(username *models.Username) error\n\tDelete(username *models.Username) error\n}\n\ntype usernamePersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewUsernamePersister(db *pop.Connection) UsernamePersister {\n\treturn &usernamePersister{db: db}\n}\n\nfunc (p *usernamePersister) Create(username models.Username) error {\n\tvErr, err := p.db.ValidateAndCreate(&username)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store username credential: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"username object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *usernamePersister) GetByName(username string) (*models.Username, error) {\n\tpw := models.Username{}\n\tquery := p.db.Where(\"username = (?)\", username)\n\terr := query.First(&pw)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get username: %w\", err)\n\t}\n\treturn &pw, nil\n}\n\nfunc (p *usernamePersister) Update(username *models.Username) error {\n\tvErr, err := p.db.ValidateAndUpdate(username)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update username: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"username object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *usernamePersister) Delete(username *models.Username) error {\n\terr := p.db.Destroy(username)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete user: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/webauthn_credential_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnCredentialPersister interface {\n\tGet(string) (*models.WebauthnCredential, error)\n\tCreate(models.WebauthnCredential) error\n\tUpdate(models.WebauthnCredential) error\n\tDelete(models.WebauthnCredential) error\n\tGetFromUser(uuid.UUID) (models.WebauthnCredentials, error)\n}\n\ntype webauthnCredentialPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewWebauthnCredentialPersister(db *pop.Connection) WebauthnCredentialPersister {\n\treturn &webauthnCredentialPersister{db: db}\n}\n\nfunc (p *webauthnCredentialPersister) Get(id string) (*models.WebauthnCredential, error) {\n\tcredential := models.WebauthnCredential{}\n\terr := p.db.Eager().Find(&credential, id)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get credential: %w\", err)\n\t}\n\n\treturn &credential, nil\n}\n\n// Create stores a new `WebauthnCredential`. Please run inside a transaction, since `Transports` associated with the\n// credential are stored separately in another table.\nfunc (p *webauthnCredentialPersister) Create(credential models.WebauthnCredential) error {\n\tif credential.UserHandle != nil {\n\t\tvErr, err := p.db.ValidateAndCreate(credential.UserHandle)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to store credential user handle: %w\", err)\n\t\t}\n\t\tif vErr != nil && vErr.HasAny() {\n\t\t\treturn fmt.Errorf(\"credential object validation failed: %w\", vErr)\n\t\t}\n\t}\n\n\tvErr, err := p.db.ValidateAndCreate(&credential)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store credential: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"credential object validation failed: %w\", vErr)\n\t}\n\n\t// Eager creation seems to be broken, so we need to store the transports separately.\n\t// See: https://github.com/gobuffalo/pop/issues/608\n\tvErr, err = p.db.ValidateAndCreate(&credential.Transports)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store credential transport: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"credential transport validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *webauthnCredentialPersister) Update(credential models.WebauthnCredential) error {\n\tvErr, err := p.db.ValidateAndUpdate(&credential)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update credential: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"credential object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *webauthnCredentialPersister) Delete(credential models.WebauthnCredential) error {\n\terr := p.db.Destroy(&credential)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete credential: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (p *webauthnCredentialPersister) GetFromUser(userId uuid.UUID) (models.WebauthnCredentials, error) {\n\tvar credentials []models.WebauthnCredential\n\terr := p.db.Eager().Where(\"user_id = ?\", &userId).Order(\"created_at asc\").All(&credentials)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn credentials, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get credentials: %w\", err)\n\t}\n\n\treturn credentials, nil\n}\n"
  },
  {
    "path": "backend/persistence/webauthn_credential_user_handle_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebauthnCredentialUserHandlePersister interface {\n\tGetByHandle(string) (*models.WebauthnCredentialUserHandle, error)\n}\n\nfunc NewWebauthnCredentialUserHandlePersister(db *pop.Connection) WebauthnCredentialUserHandlePersister {\n\treturn &webauthnCredentialUserHandlePersister{db: db}\n}\n\ntype webauthnCredentialUserHandlePersister struct {\n\tdb *pop.Connection\n}\n\nfunc (p *webauthnCredentialUserHandlePersister) GetByHandle(handle string) (*models.WebauthnCredentialUserHandle, error) {\n\thandleModel := models.WebauthnCredentialUserHandle{}\n\terr := p.db.Where(\"handle = ?\", handle).First(&handleModel)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get handleModel: %w\", err)\n\t}\n\n\treturn &handleModel, nil\n}\n"
  },
  {
    "path": "backend/persistence/webauthn_session_data_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\ntype WebauthnSessionDataPersister interface {\n\tGet(id uuid.UUID) (*models.WebauthnSessionData, error)\n\tGetByChallenge(challenge string) (*models.WebauthnSessionData, error)\n\tCreate(sessionData models.WebauthnSessionData) error\n\tUpdate(sessionData models.WebauthnSessionData) error\n\tDelete(sessionData models.WebauthnSessionData) error\n\tCleanup[models.WebauthnSessionData]\n}\n\ntype webauthnSessionDataPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewWebauthnSessionDataPersister(db *pop.Connection) WebauthnSessionDataPersister {\n\treturn &webauthnSessionDataPersister{db: db}\n}\n\nfunc (p *webauthnSessionDataPersister) Get(id uuid.UUID) (*models.WebauthnSessionData, error) {\n\tsessionData := models.WebauthnSessionData{}\n\terr := p.db.Eager().Find(&sessionData, id)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get sessionData: %w\", err)\n\t}\n\n\treturn &sessionData, nil\n}\n\nfunc (p *webauthnSessionDataPersister) GetByChallenge(challenge string) (*models.WebauthnSessionData, error) {\n\tvar sessionData []models.WebauthnSessionData\n\terr := p.db.Eager().Where(\"challenge = ?\", challenge).All(&sessionData)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get sessionData: %w\", err)\n\t}\n\n\tif len(sessionData) <= 0 {\n\t\treturn nil, nil\n\t}\n\n\treturn &sessionData[0], nil\n}\n\nfunc (p *webauthnSessionDataPersister) Create(sessionData models.WebauthnSessionData) error {\n\tvErr, err := p.db.Eager().ValidateAndCreate(&sessionData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to store sessionData: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"sessionData object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *webauthnSessionDataPersister) Update(sessionData models.WebauthnSessionData) error {\n\tvErr, err := p.db.Eager().ValidateAndUpdate(&sessionData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update sessionData: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"sessionData object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (p *webauthnSessionDataPersister) FindExpired(cutoffTime time.Time, page, perPage int) ([]models.WebauthnSessionData, error) {\n\tvar items []models.WebauthnSessionData\n\n\tquery := p.db.\n\t\tWhere(\"expires_at < ?\", cutoffTime).\n\t\tSelect(\"id\").\n\t\tPaginate(page, perPage)\n\terr := query.All(&items)\n\n\treturn items, err\n}\n\nfunc (p *webauthnSessionDataPersister) Delete(sessionData models.WebauthnSessionData) error {\n\terr := p.db.Destroy(&sessionData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete sessionData: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/persistence/webhook_persister.go",
    "content": "package persistence\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\ntype WebhookPersister interface {\n\tCreate(webhook models.Webhook, events models.WebhookEvents) error\n\tUpdate(webhook models.Webhook) error\n\tDelete(webhook models.Webhook) error\n\tAddEvent(event models.WebhookEvent) error\n\tRemoveEvent(event models.WebhookEvent) error\n\tList(includeDisabled bool) (models.Webhooks, error)\n\tGet(webhookId uuid.UUID) (*models.Webhook, error)\n}\n\ntype webhookPersister struct {\n\tdb *pop.Connection\n}\n\nfunc NewWebhookPersister(db *pop.Connection) WebhookPersister {\n\treturn &webhookPersister{db: db}\n}\n\nfunc (w *webhookPersister) Create(webhook models.Webhook, events models.WebhookEvents) error {\n\tvErr, err := w.db.ValidateAndCreate(&webhook)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create webhook: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"webhook object validation failed: %w\", vErr)\n\t}\n\n\tfor _, event := range events {\n\t\tvErr, err = w.db.ValidateAndCreate(&event)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create webhook event: %w\", err)\n\t\t}\n\n\t\tif vErr != nil && vErr.HasAny() {\n\t\t\treturn fmt.Errorf(\"webhook event object validation failed: %w\", vErr)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (w *webhookPersister) Update(webhook models.Webhook) error {\n\tvErr, err := w.db.ValidateAndUpdate(&webhook)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update webhook: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"webhook object validation failed: %w\", vErr)\n\t}\n\n\tfor _, event := range webhook.WebhookEvents {\n\t\texists, err := w.db.Where(\"id = ?\", event.ID).Exists(&models.WebhookEvent{})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to check for existing webhook event: %w\", err)\n\t\t}\n\n\t\tif exists {\n\t\t\tcontinue\n\t\t}\n\n\t\tvErr, err = w.db.ValidateAndCreate(&event)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create webhook event during update: %w\", err)\n\t\t}\n\n\t\tif vErr != nil && vErr.HasAny() {\n\t\t\treturn fmt.Errorf(\"webhook event object validation failed during update: %w\", vErr)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (w *webhookPersister) Delete(webhook models.Webhook) error {\n\terr := w.db.Destroy(&webhook)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete webhook: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (w *webhookPersister) AddEvent(event models.WebhookEvent) error {\n\tvErr, err := w.db.ValidateAndCreate(&event)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create webhook event: %w\", err)\n\t}\n\n\tif vErr != nil && vErr.HasAny() {\n\t\treturn fmt.Errorf(\"webhook event object validation failed: %w\", vErr)\n\t}\n\n\treturn nil\n}\n\nfunc (w *webhookPersister) RemoveEvent(event models.WebhookEvent) error {\n\terr := w.db.Destroy(&event)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to remove webhook event: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (w *webhookPersister) List(includeDisabled bool) (models.Webhooks, error) {\n\twebhooks := make(models.Webhooks, 0)\n\tquery := w.db.Eager().Q()\n\tif !includeDisabled {\n\t\tquery = query.Where(\"enabled = true\")\n\t}\n\terr := query.All(&webhooks)\n\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn webhooks, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fetch webhooks: %w\", err)\n\t}\n\n\treturn webhooks, nil\n}\n\nfunc (w *webhookPersister) Get(webhookId uuid.UUID) (*models.Webhook, error) {\n\twebhook := models.Webhook{}\n\terr := w.db.Eager().Find(&webhook, webhookId)\n\tif err != nil && errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get webhook: %w\", err)\n\t}\n\n\treturn &webhook, nil\n}\n"
  },
  {
    "path": "backend/rate_limiter/rate_limiter.go",
    "content": "package rate_limiter\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/gomodule/redigo/redis\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/sethvargo/go-limiter\"\n\t\"github.com/sethvargo/go-limiter/httplimit\"\n\t\"github.com/sethvargo/go-limiter/memorystore\"\n\t\"github.com/sethvargo/go-redisstore\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n\t\"math\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n)\n\nfunc NewRateLimiter(cfg config.RateLimiter, limits config.RateLimits) limiter.Store {\n\tif cfg.Store == config.RATE_LIMITER_STORE_REDIS {\n\t\tstore, err := redisstore.New(&redisstore.Config{\n\t\t\tTokens:   limits.Tokens,\n\t\t\tInterval: limits.Interval,\n\t\t\tDial: func() (redis.Conn, error) {\n\t\t\t\treturn redis.Dial(\"tcp\", cfg.Redis.Address,\n\t\t\t\t\tredis.DialPassword(cfg.Redis.Password))\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\treturn store\n\t}\n\t// else return in_memory\n\tstore, err := memorystore.New(&memorystore.Config{\n\t\tTokens:   limits.Tokens,\n\t\tInterval: limits.Interval,\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn store\n}\n\nfunc Limit(store limiter.Store, userId uuid.UUID, c echo.Context) error {\n\tkey := c.Path() + \"/\" + userId.String() + \"/\" + c.RealIP()\n\t// Take from the store.\n\tlimit, remaining, reset, ok, err := store.Take(context.Background(), key)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresetTime := int(math.Floor(time.Unix(0, int64(reset)).UTC().Sub(time.Now().UTC()).Seconds()))\n\n\t// Set headers (we do this regardless of whether the request is permitted).\n\tc.Response().Header().Set(httplimit.HeaderRateLimitLimit, strconv.FormatUint(limit, 10))\n\tc.Response().Header().Set(httplimit.HeaderRateLimitRemaining, strconv.FormatUint(remaining, 10))\n\tc.Response().Header().Set(httplimit.HeaderRateLimitReset, strconv.Itoa(resetTime))\n\n\t// Fail if there were no tokens remaining.\n\tif !ok {\n\t\tc.Response().Header().Set(httplimit.HeaderRetryAfter, strconv.Itoa(resetTime))\n\t\treturn echo.NewHTTPError(http.StatusTooManyRequests)\n\t}\n\treturn nil\n}\n\nfunc Limit2(store limiter.Store, key string) (int, bool, error) {\n\t// Take from the store.\n\t_, _, newTokensAvailableAt, ok, err := store.Take(context.Background(), key)\n\tif err != nil {\n\t\treturn -1, false, fmt.Errorf(\"failed to take a token from %s\", key)\n\t}\n\n\tretryAfterSeconds := int(math.Floor(time.Unix(0, int64(newTokensAvailableAt)).UTC().Sub(time.Now().UTC()).Seconds()))\n\n\treturn retryAfterSeconds, ok, nil\n}\n\nfunc CreateRateLimitPasscodeKey(realIP, email string) string {\n\treturn fmt.Sprintf(\"passcode/%s/%s\", realIP, email)\n}\n\nfunc CreateRateLimitPasswordKey(realIP, userId string) string {\n\treturn fmt.Sprintf(\"password/%s/%s\", realIP, userId)\n}\n\nfunc CreateRateLimitOTPKey(realIP, userId string) string {\n\treturn fmt.Sprintf(\"otp/%s/%s\", realIP, userId)\n}\n\nfunc CreateRateLimitTokenExchangeKey(realIP string) string {\n\treturn fmt.Sprintf(\"token_exchange/%s\", realIP)\n}\n"
  },
  {
    "path": "backend/rate_limiter/rate_limiter_test.go",
    "content": "package rate_limiter\n\nimport (\n\t\"context\"\n\t\"github.com/rs/zerolog/log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestNewRateLimiter(t *testing.T) {\n\tcfg := config.RateLimiter{\n\t\tEnabled: true,\n\t\tStore:   config.RATE_LIMITER_STORE_IN_MEMORY,\n\t\tRedis:   nil,\n\t}\n\n\trl := NewRateLimiter(cfg, config.RateLimits{\n\t\tTokens:   5,\n\t\tInterval: 1 * time.Minute,\n\t})\n\t// Take 5 tokens: should be good.\n\tfor i := 0; i < 5; i++ {\n\t\ttokens, remaining, reset, ok, e := rl.Take(context.Background(), \"some-key\")\n\t\tlog.Printf(\"Tokens: %v, Remaining: %v, Reset: %v, ok: %v, error: %v\\n\", tokens, remaining, time.Unix(0, int64(reset)).String(), ok, e)\n\t\tif e != nil {\n\t\t\tt.Error(e)\n\t\t}\n\t\tif !ok {\n\t\t\tt.Error(\"Taking a token should succeed at this point.\")\n\t\t}\n\t}\n\n\ttokens, remaining, reset, ok, e := rl.Take(context.Background(), \"some-key\")\n\tlog.Printf(\"Tokens: %v, Remaining: %v, Reset: %v, ok: %v, error: %v\\n\", tokens, remaining, time.Unix(0, int64(reset)).String(), ok, e)\n\tif ok {\n\t\tt.Error(\"Taking a token should fail at this point\")\n\t}\n}\n"
  },
  {
    "path": "backend/server/server.go",
    "content": "package server\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/handler\"\n\t\"github.com/teamhanko/hanko/backend/v2/mapper\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"sync\"\n)\n\nfunc StartPublic(cfg *config.Config, wg *sync.WaitGroup, persister persistence.Persister, prometheus echo.MiddlewareFunc, authenticatorMetadata mapper.AuthenticatorMetadata) {\n\tdefer wg.Done()\n\trouter := handler.NewPublicRouter(cfg, persister, prometheus, authenticatorMetadata)\n\trouter.Logger.Fatal(router.Start(cfg.Server.Public.Address))\n}\n\nfunc StartAdmin(cfg *config.Config, wg *sync.WaitGroup, persister persistence.Persister, prometheus echo.MiddlewareFunc) {\n\tdefer wg.Done()\n\trouter := handler.NewAdminRouter(cfg, persister, prometheus)\n\trouter.Logger.Fatal(router.Start(cfg.Server.Admin.Address))\n}\n"
  },
  {
    "path": "backend/session/session.go",
    "content": "package session\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n)\n\ntype Manager interface {\n\tGenerateJWT(user dto.UserJWT, opts ...JWTOptions) (string, jwt.Token, error)\n\tVerify(string) (jwt.Token, error)\n\tGenerateCookie(token string) (*http.Cookie, error)\n\tDeleteCookie() (*http.Cookie, error)\n}\n\n// Manager is used to create and verify session JWTs\ntype manager struct {\n\tjwtGenerator  jwk.Generator\n\tsessionLength time.Duration\n\tcookieConfig  cookieConfig\n\tissuer        string\n\taudience      []string\n\tjwtTemplate   *config.JWTTemplate\n}\n\ntype cookieConfig struct {\n\tName     string\n\tDomain   string\n\tHttpOnly bool\n\tSameSite http.SameSite\n\tSecure   bool\n}\n\nconst (\n\tGeneratorCreateFailure = \"failed to create session generator: %w\"\n)\n\n// NewManager returns a new Manager which will be used to create and verify sessions JWTs\nfunc NewManager(jwtGenerator jwk.Generator, config config.Config) (Manager, error) {\n\tduration, _ := time.ParseDuration(config.Session.Lifespan) // error can be ignored, value is checked in config validation\n\tsameSite := http.SameSite(0)\n\tswitch config.Session.Cookie.SameSite {\n\tcase \"lax\":\n\t\tsameSite = http.SameSiteLaxMode\n\tcase \"strict\":\n\t\tsameSite = http.SameSiteStrictMode\n\tcase \"none\":\n\t\tsameSite = http.SameSiteNoneMode\n\tdefault:\n\t\tsameSite = http.SameSiteDefaultMode\n\t}\n\tvar audience []string\n\tif config.Session.Audience != nil && len(config.Session.Audience) > 0 {\n\t\taudience = config.Session.Audience\n\t} else {\n\t\taudience = []string{config.Webauthn.RelyingParty.Id}\n\t}\n\n\treturn &manager{\n\t\tjwtGenerator:  jwtGenerator,\n\t\tsessionLength: duration,\n\t\tissuer:        config.Session.Issuer,\n\t\tcookieConfig: cookieConfig{\n\t\t\tName:     config.Session.Cookie.GetName(),\n\t\t\tDomain:   config.Session.Cookie.Domain,\n\t\t\tHttpOnly: config.Session.Cookie.HttpOnly,\n\t\t\tSameSite: sameSite,\n\t\t\tSecure:   config.Session.Cookie.Secure,\n\t\t},\n\t\taudience:    audience,\n\t\tjwtTemplate: config.Session.JWTTemplate,\n\t}, nil\n}\n\n// GenerateJWT creates a new session JWT for the given user\nfunc (m *manager) GenerateJWT(user dto.UserJWT, opts ...JWTOptions) (string, jwt.Token, error) {\n\ttoken := jwt.New()\n\n\t// Process the claim template if found\n\tif m.jwtTemplate != nil {\n\t\tif err := ProcessJWTTemplate(token, m.jwtTemplate.Claims, user); err != nil {\n\t\t\treturn \"\", nil, err\n\t\t}\n\t}\n\n\tissuedAt := time.Now()\n\texpiration := issuedAt.Add(m.sessionLength)\n\n\t_ = token.Set(jwt.SubjectKey, user.UserID)\n\t_ = token.Set(jwt.IssuedAtKey, issuedAt)\n\t_ = token.Set(jwt.ExpirationKey, expiration)\n\t_ = token.Set(jwt.AudienceKey, m.audience)\n\n\tsessionID, err := uuid.NewV4()\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\t_ = token.Set(\"session_id\", sessionID.String())\n\n\tif user.Email != nil {\n\t\t_ = token.Set(\"email\", user.Email)\n\t}\n\n\tif user.Username != \"\" {\n\t\t_ = token.Set(\"username\", user.Username)\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(token)\n\t}\n\n\tif m.issuer != \"\" {\n\t\t_ = token.Set(jwt.IssuerKey, m.issuer)\n\t}\n\n\tsigned, err := m.jwtGenerator.Sign(token)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\treturn string(signed), token, nil\n}\n\n// Verify verifies the given JWT and returns a parsed one if verification was successful\nfunc (m *manager) Verify(token string) (jwt.Token, error) {\n\tparsedToken, err := m.jwtGenerator.Verify([]byte(token))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to verify session token: %w\", err)\n\t}\n\n\treturn parsedToken, nil\n}\n\n// GenerateCookie creates a new session cookie for the given user\nfunc (m *manager) GenerateCookie(token string) (*http.Cookie, error) {\n\treturn &http.Cookie{\n\t\tName:     m.cookieConfig.Name,\n\t\tValue:    token,\n\t\tDomain:   m.cookieConfig.Domain,\n\t\tPath:     \"/\",\n\t\tSecure:   m.cookieConfig.Secure,\n\t\tHttpOnly: m.cookieConfig.HttpOnly,\n\t\tSameSite: m.cookieConfig.SameSite,\n\t\tMaxAge:   int(m.sessionLength.Seconds()),\n\t}, nil\n}\n\n// DeleteCookie returns a cookie that will expire the cookie on the frontend\nfunc (m *manager) DeleteCookie() (*http.Cookie, error) {\n\treturn &http.Cookie{\n\t\tName:     m.cookieConfig.Name,\n\t\tValue:    \"\",\n\t\tDomain:   m.cookieConfig.Domain,\n\t\tPath:     \"/\",\n\t\tSecure:   m.cookieConfig.Secure,\n\t\tHttpOnly: m.cookieConfig.HttpOnly,\n\t\tSameSite: m.cookieConfig.SameSite,\n\t\tMaxAge:   -1,\n\t}, nil\n}\n\ntype JWTOptions func(token jwt.Token)\n\nfunc WithValue(key string, value interface{}) JWTOptions {\n\treturn func(jwt jwt.Token) {\n\t\t_ = jwt.Set(key, value)\n\t}\n}\n"
  },
  {
    "path": "backend/session/session_test.go",
    "content": "package session\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n)\n\nfunc TestNewGenerator(t *testing.T) {\n\tmanager := test.JwkManager{}\n\tcfg := config.Config{}\n\tsessionGenerator, err := NewManager(&manager, cfg)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, sessionGenerator)\n}\n\nfunc TestGenerator_Generate(t *testing.T) {\n\tmanager := test.JwkManager{}\n\tcfg := config.Config{}\n\tsessionGenerator, err := NewManager(&manager, cfg)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, sessionGenerator)\n\n\tuserId, err := uuid.NewV4()\n\tassert.NoError(t, err)\n\n\tsession, _, err := sessionGenerator.GenerateJWT(dto.UserJWT{\n\t\tUserID: userId.String(),\n\t})\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, session)\n}\n\nfunc TestGenerator_Verify(t *testing.T) {\n\tsessionLifespan := \"5m\"\n\tmanager := test.JwkManager{}\n\tcfg := config.Config{\n\t\tSession: config.Session{Lifespan: sessionLifespan},\n\t}\n\tsessionGenerator, err := NewManager(&manager, cfg)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, sessionGenerator)\n\n\tuserId, err := uuid.NewV4()\n\tassert.NoError(t, err)\n\n\ttestEmail := \"lorem@ipsum.local\"\n\n\temailDto := &dto.EmailJWT{\n\t\tAddress:    testEmail,\n\t\tIsPrimary:  true,\n\t\tIsVerified: false,\n\t}\n\n\tsession, _, err := sessionGenerator.GenerateJWT(dto.UserJWT{\n\t\tUserID: userId.String(),\n\t\tEmail:  emailDto,\n\t})\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, session)\n\n\ttoken, err := sessionGenerator.Verify(session)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, token)\n\tassert.Equal(t, token.Subject(), userId.String())\n\tassert.False(t, time.Time{}.Equal(token.IssuedAt()))\n\tassert.False(t, time.Time{}.Equal(token.Expiration()))\n\n\temailClaim, ok := token.Get(\"email\")\n\tassert.True(t, ok)\n\tassert.NotNil(t, emailClaim)\n\n\t// Workaround as .(EmailJWT) interface conversion is not possible\n\temailJson, _ := json.Marshal(emailClaim)\n\tvar tokenEmail dto.EmailJWT\n\t_ = json.Unmarshal(emailJson, &tokenEmail)\n\n\tassert.Equal(t, testEmail, tokenEmail.Address)\n\tassert.True(t, tokenEmail.IsPrimary)\n\tassert.False(t, tokenEmail.IsVerified)\n\n\tsessionDuration, _ := time.ParseDuration(sessionLifespan)\n\tassert.True(t, token.IssuedAt().Add(sessionDuration).Equal(token.Expiration()))\n}\n\nfunc TestManager_GenerateJWT_IssAndAud(t *testing.T) {\n\tmanager := test.JwkManager{}\n\tcfg := config.Config{\n\t\tSession: config.Session{\n\t\t\tIssuer:   \"hanko\",\n\t\t\tLifespan: \"5m\",\n\t\t},\n\t\tWebauthn: config.WebauthnSettings{\n\t\t\tRelyingParty: config.RelyingParty{\n\t\t\t\tId: \"test.hanko.io\",\n\t\t\t},\n\t\t},\n\t}\n\tsessionGenerator, err := NewManager(&manager, cfg)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, sessionGenerator)\n\n\tuserId, _ := uuid.NewV4()\n\tj, _, err := sessionGenerator.GenerateJWT(dto.UserJWT{\n\t\tUserID: userId.String(),\n\t})\n\tassert.NoError(t, err)\n\n\ttoken, err := jwt.ParseString(j, jwt.WithVerify(false))\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\"test.hanko.io\"}, token.Audience())\n\tassert.Equal(t, \"hanko\", token.Issuer())\n}\n\nfunc TestManager_GenerateJWT_AdditionalAudiences(t *testing.T) {\n\tmanager := test.JwkManager{}\n\tcfg := config.Config{\n\t\tSession: config.Session{\n\t\t\tIssuer:   \"hanko\",\n\t\t\tLifespan: \"5m\",\n\t\t\tAudience: []string{\n\t\t\t\t\"additional.hanko.io\",\n\t\t\t\t\"anotherOne\",\n\t\t\t},\n\t\t},\n\t\tWebauthn: config.WebauthnSettings{\n\t\t\tRelyingParty: config.RelyingParty{\n\t\t\t\tId: \"test.hanko.io\",\n\t\t\t},\n\t\t},\n\t}\n\tsessionGenerator, err := NewManager(&manager, cfg)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, sessionGenerator)\n\n\tuserId, _ := uuid.NewV4()\n\tj, _, err := sessionGenerator.GenerateJWT(dto.UserJWT{\n\t\tUserID: userId.String(),\n\t})\n\tassert.NoError(t, err)\n\n\ttoken, err := jwt.ParseString(j, jwt.WithVerify(false))\n\tassert.NoError(t, err)\n\tassert.Equal(t, []string{\n\t\t\"additional.hanko.io\",\n\t\t\"anotherOne\",\n\t}, token.Audience())\n\tassert.Equal(t, \"hanko\", token.Issuer())\n}\n\nfunc Test_GenerateJWT_SessionID(t *testing.T) {\n\ttests := []struct {\n\t\tname                     string\n\t\tconfig                   config.Config\n\t\ttokenShouldHaveSessionID bool\n\t}{\n\t\t{\n\t\t\tname: \"token has a session id claim\",\n\t\t\tconfig: config.Config{\n\t\t\t\tSession: config.Session{Lifespan: \"5m\"},\n\t\t\t},\n\t\t\ttokenShouldHaveSessionID: true,\n\t\t},\n\t}\n\tfor _, testData := range tests {\n\t\tt.Run(testData.name, func(t *testing.T) {\n\t\t\tjwkManager := test.JwkManager{}\n\t\t\tsessionGenerator, err := NewManager(&jwkManager, testData.config)\n\t\t\tassert.NoError(t, err)\n\t\t\trequire.NotEmpty(t, sessionGenerator)\n\n\t\t\tuserId, _ := uuid.NewV4()\n\t\t\ttokenString, _, err := sessionGenerator.GenerateJWT(dto.UserJWT{\n\t\t\t\tUserID: userId.String(),\n\t\t\t})\n\t\t\tassert.NoError(t, err)\n\n\t\t\ttoken, err := jwt.ParseString(tokenString, jwt.WithVerify(false))\n\t\t\trawSessionID, tokenHasSessionID := token.Get(\"session_id\")\n\t\t\tassert.NoError(t, err)\n\n\t\t\tassert.Equal(t, testData.tokenShouldHaveSessionID, tokenHasSessionID)\n\n\t\t\tif testData.tokenShouldHaveSessionID {\n\t\t\t\tsessionIDstring, ok := rawSessionID.(string)\n\t\t\t\tassert.True(t, ok)\n\t\t\t\tsessionID := uuid.FromStringOrNil(sessionIDstring)\n\t\t\t\tassert.NotEqual(t, sessionID, uuid.Nil)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGenerator_Verify_Error(t *testing.T) {\n\tmanager := test.JwkManager{}\n\tcfg := config.Config{}\n\tsessionGenerator, err := NewManager(&manager, cfg)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, sessionGenerator)\n\n\ttests := []struct {\n\t\tName    string\n\t\tInput   string\n\t\tWantErr error\n\t}{\n\t\t{\n\t\t\tName:  \"expired\",\n\t\t\tInput: \"eyJhbGciOiJSUzI1NiIsImtpZCI6ImtleTEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NTIxNzM4NTAsImlhdCI6MTY1MjE3Mzc5MCwic3ViIjoiYzU0YTZlZTUtZjNmMy00YzExLTlhMWMtYWUzOTgyYTQxMzYyIn0.AEzZ0M1_3HnOtqd8Dz-BliHkEUc4c5mu97eXhoErgG7qbVWisJP0qfz_KrwL9VYFOYuDAmfRZ3ABnaOg-S53wlRndfL-ulk68lY34otGZfXKhk2P3GJRH8Dq7hW83KnwkSPF5_iOaIIDfUwrWOaavvtLJFgg5RcehuwLkYEA5X17ek6cUNsqz7Vw-x2REReh_f31f5zneqKN9CeVnup5_ZgtMYpOXVvXAORs3b7y2oMwFdXs-hVal9ZVunNPo3iZmaTFMHUSNXX8MceOy_dUofxtd9JDzliiPrjNWDjU5Jx5paLBA5CUc4SctBURi2oJABbkeE1l4ug6-rTOYB04-UW8XAnPZONBTnv3AjtzvScvkpUj-OFKVQLGgcXZHUo1J7ftLaezpWrGTbhlC8TVvXdX1ms5w9D1uqEUZ94lhvVSW_lGGX2DGqMWaT6tOcSpDHFQ0NR5FD3MiNGV-z43AUOOSzilAKS2WaHDS7v43PeJ75xzAAS_7xOoc6L3Z9msdToQIauLYuCrivoOVcCqrEHugknpxO8M0xo6f1fHws8RocT3S7B76YJUIBeAj2F31wJne5xtbiRF5GWiV8uS3ZTXqrPp7y4U6Btf-h6mvEos_Q9o9w5hck-8lixUs5mObPDsT-W6PdEehRaSL7-13dy1GpB8wMP5fGlnRSff9y4\",\n\t\t},\n\t\t{\n\t\t\tName:  \"wrong signature\",\n\t\t\tInput: \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAiLCJleHAiOjE2NDg2MjgzODAsImlhdCI6MTY0ODYyNDc4MCwiaXNzIjoiSG90M3RlOWlJbHVwbm5scDBld1E1RWJmIiwic3ViIjoiaU9ielRWdUc2bjl6Um1TY2ZMb0RFT1NiIn0.QZPEyEaGCJikNP2slVTGdsT3x8CuT4ynd5tdj-7c38Aa54277MPgGbapQ7JGrvwyjhAihzvvlqCxn2oX3zIFdu0HmSlxAXQ6Ah1K0KlQabneG8XNSed3sgp9xM0BYV1rB2SCuyXwE3U3zj5zFc4g4-v2Y1hpn7z4n3n9IlnnShK7NTUaaELlWPD8FQyp8mzZmJVSDoWbCMdywGHkX5ZWMUAwPfvC17kYZj6nqXC5ZJm3i2u-488cDeE5NxCFe-0ey14NtNtM9xTaPy5U8zvoqeCik1-ZNbxR_NJC4H25Cth2pm__e-W4KepGy7i-cLZ1T_DqNNk8HX9zX_Quj88FJw\",\n\t\t},\n\t}\n\n\tfor _, verifyTest := range tests {\n\t\tt.Run(verifyTest.Name, func(t *testing.T) {\n\t\t\t_, err := sessionGenerator.Verify(verifyTest.Input)\n\t\t\tassert.Error(t, err)\n\t\t})\n\t}\n}\n\nfunc TestGenerator_DeleteCookie(t *testing.T) {\n\tmanager := test.JwkManager{}\n\tcfg := config.Config{}\n\tsessionGenerator, err := NewManager(&manager, cfg)\n\tassert.NoError(t, err)\n\trequire.NotEmpty(t, sessionGenerator)\n\n\tcookie, err := sessionGenerator.DeleteCookie()\n\tassert.NoError(t, err)\n\tassert.Equal(t, -1, cookie.MaxAge)\n\tassert.Equal(t, \"hanko\", cookie.Name)\n}\n"
  },
  {
    "path": "backend/session/template.go",
    "content": "package session\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/template\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/rs/zerolog/log\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n)\n\n// JWTTemplateData holds the data available for template processing\ntype JWTTemplateData struct {\n\tUser *dto.UserJWT\n}\n\n// ProcessJWTTemplate processes a map of claims using the provided user data and sets them on the token\nfunc ProcessJWTTemplate(token jwt.Token, claims map[string]interface{}, user dto.UserJWT) error {\n\tclaimTemplateData := JWTTemplateData{\n\t\tUser: &user,\n\t}\n\tfor key, value := range claims {\n\t\tprocessedValue, err := processClaimTemplate(value, claimTemplateData)\n\t\tif err != nil {\n\t\t\tlog.Warn().Err(err).Str(\"session\", key).Msgf(\"failed to process custom JWT claim template: %+v\", value)\n\t\t\tcontinue\n\t\t}\n\t\terr = token.Set(key, processedValue)\n\t\tif err != nil {\n\t\t\tlog.Warn().Err(err).Str(\"session\", key).Msgf(\"failed to set processed JWT claim %+v to token\", value)\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn nil\n}\n\n// processClaimTemplate processes a claim value, handling both string templates and nested structures\nfunc processClaimTemplate(value interface{}, data JWTTemplateData) (interface{}, error) {\n\tswitch v := value.(type) {\n\tcase string:\n\t\treturn parseClaimTemplateValue(v, data)\n\tcase map[string]interface{}:\n\t\tresult := make(map[string]interface{})\n\t\tfor key, val := range v {\n\t\t\tprocessed, err := processClaimTemplate(val, data)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresult[key] = processed\n\t\t}\n\t\treturn result, nil\n\tcase []interface{}:\n\t\tresult := make([]interface{}, len(v))\n\t\tfor i, val := range v {\n\t\t\tprocessed, err := processClaimTemplate(val, data)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresult[i] = processed\n\t\t}\n\t\treturn result, nil\n\tdefault:\n\t\treturn value, nil\n\t}\n}\n\n// parseClaimTemplateValue parses and executes a template string using the provided data\nfunc parseClaimTemplateValue(tmplStr string, data JWTTemplateData) (interface{}, error) {\n\ttmpl, err := template.New(\"\").Parse(tmplStr)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to parse template: %w\", err)\n\t}\n\n\tvar buf bytes.Buffer\n\tif err = tmpl.Execute(&buf, data); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to execute template: %w\", err)\n\t}\n\n\tresultString := buf.String()\n\n\t// \"Workaround\"/\"hack\" for when the template expression evaluates to a boolean string, i.e. \"true\"\n\t// or \"false\". This converts it to a bool for consistency's sake (i.e. to prevent that both boolean\n\t// values and boolean strings are eventually set in the JWT).\n\tif resultString == \"true\" || resultString == \"false\" {\n\t\tb, err := strconv.ParseBool(resultString)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not parse string as bool: %w\", err)\n\t\t}\n\t\treturn b, nil\n\t}\n\n\t// Another workaround for JSON objects and arrays. Somewhere along the way, representations\n\t// of JSON objects and arrays end up with multiple escape characters. This was the only way\n\t// to properly get rid of them.\n\tlooksLikeObject := strings.HasPrefix(resultString, \"{\") && strings.HasSuffix(resultString, \"}\")\n\tlooksLikeArray := strings.HasPrefix(resultString, \"[\") && strings.HasSuffix(resultString, \"]\")\n\tif looksLikeObject || looksLikeArray {\n\t\tvar result interface{}\n\t\tif err := json.Unmarshal([]byte(resultString), &result); err == nil {\n\t\t\treturn result, nil\n\t\t}\n\t}\n\n\treturn resultString, nil\n}\n"
  },
  {
    "path": "backend/session/template_test.go",
    "content": "package session\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\t\"testing\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n)\n\nfunc TestProcessJWTTemplate(t *testing.T) {\n\ttests := []struct {\n\t\tname           string\n\t\tclaims         map[string]interface{}\n\t\tuser           dto.UserJWT\n\t\texpectedClaims json.RawMessage\n\t\twantErr        bool\n\t}{\n\t\t{\n\t\t\tname: \"should process static claims with basic and complex types\",\n\t\t\tclaims: map[string]interface{}{\n\t\t\t\t\"static_str\":      \"foo\",\n\t\t\t\t\"static_num\":      123,\n\t\t\t\t\"static_bool\":     true,\n\t\t\t\t\"static_str_arr\":  []string{\"a\", \"b\", \"c\"},\n\t\t\t\t\"static_num_arr\":  []int{1, 2, 3},\n\t\t\t\t\"static_bool_arr\": []bool{true, false, true},\n\t\t\t\t\"static_mix_arr\":  []interface{}{\"a\", 1, true},\n\t\t\t\t\"static_obj\": struct {\n\t\t\t\t\tA string\n\t\t\t\t\tB []string\n\t\t\t\t\tC struct {\n\t\t\t\t\t\tD []interface{}\n\t\t\t\t\t}\n\t\t\t\t}{\n\t\t\t\t\tA: \"a\",\n\t\t\t\t\tB: []string{\"a\", \"b\", \"c\"},\n\t\t\t\t\tC: struct {\n\t\t\t\t\t\tD []interface{}\n\t\t\t\t\t}{\n\t\t\t\t\t\tD: []interface{}{\"a\", 1, true},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tuser: dto.UserJWT{\n\t\t\t\tEmail: &dto.EmailJWT{\n\t\t\t\t\tAddress: \"test@example.com\",\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectedClaims: json.RawMessage(`{\n\t\t\t\t\"static_str\": \"foo\",\n\t\t\t\t\"static_num\": 123,\n\t\t\t\t\"static_bool\": true,\n\t\t\t\t\"static_str_arr\":  [\"a\", \"b\", \"c\"],\n\t\t\t\t\"static_num_arr\":  [1, 2, 3],\n\t\t\t\t\"static_bool_arr\": [true, false, true],\n\t\t\t\t\"static_mix_arr\":  [\"a\", 1, true],\n\t\t\t\t\"static_obj\": {\n\t\t\t\t\t\"A\": \"a\",\n\t\t\t\t\t\"B\": [\"a\", \"b\", \"c\"],\n\t\t\t\t\t\"C\": {\n\t\t\t\t\t\t\"D\": [\"a\", 1, true]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}`),\n\t\t},\n\t\t{\n\t\t\tname: \"should process access to top level user context object\",\n\t\t\tclaims: map[string]interface{}{\n\t\t\t\t\"user\": \"{{ .User }}\",\n\t\t\t},\n\t\t\tuser: dto.UserJWT{\n\t\t\t\tEmail: &dto.EmailJWT{\n\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\tIsVerified: true,\n\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t},\n\t\t\t\tUsername:   \"test_user\",\n\t\t\t\tUserID:     \"48986f51-d9c8-4f22-89e9-fb7fab959399\",\n\t\t\t\tName:       \"John Doe\",\n\t\t\t\tGivenName:  \"John\",\n\t\t\t\tFamilyName: \"Doe\",\n\t\t\t\tPicture:    \"https://example.com/avatar.png\",\n\t\t\t},\n\t\t\texpectedClaims: json.RawMessage(`{\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"email\": {\n\t\t\t\t\t\t\"address\": \"test@example.com\",\n\t\t\t\t\t\t\"is_verified\": true,\n\t\t\t\t\t\t\"is_primary\": true\n\t\t\t\t\t},\n\t\t\t\t\t\"family_name\": \"Doe\",\n\t\t\t\t\t\"given_name\": \"John\",\n\t\t\t\t\t\"name\": \"John Doe\",\n\t\t\t\t\t\"picture\": \"https://example.com/avatar.png\",\n\t\t\t\t\t\"user_id\": \"48986f51-d9c8-4f22-89e9-fb7fab959399\",\n\t\t\t\t\t\"username\": \"test_user\"\n\t\t\t\t}\n\t\t\t}`),\n\t\t},\n\t\t{\n\t\t\tname: \"should process access to top level fields of user context object\",\n\t\t\tclaims: map[string]interface{}{\n\t\t\t\t\"user_id\":    \"{{ .User.UserID }}\",\n\t\t\t\t\"username\":   \"{{ .User.Username }}\",\n\t\t\t\t\"email\":      \"{{ .User.Email }}\",\n\t\t\t\t\"metadata\":   \"{{ .User.Metadata }}\",\n\t\t\t\t\"full_name\":  \"{{ .User.Name }}\",\n\t\t\t\t\"first_name\": \"{{ .User.GivenName }}\",\n\t\t\t\t\"last_name\":  \"{{ .User.FamilyName }}\",\n\t\t\t\t\"avatar\":     \"{{ .User.Picture }}\",\n\t\t\t},\n\t\t\tuser: dto.UserJWT{\n\t\t\t\tUserID:   \"48986f51-d9c8-4f22-89e9-fb7fab959399\",\n\t\t\t\tUsername: \"test_user\",\n\t\t\t\tEmail: &dto.EmailJWT{\n\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\tIsVerified: true,\n\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t},\n\t\t\t\tMetadata: dto.NewMetadataJWT(\n\t\t\t\t\tjson.RawMessage(`{\"public_key\": \"public_value\"}`),\n\t\t\t\t\tjson.RawMessage(`{\"unsafe_key\": \"unsafe_value\"}`),\n\t\t\t\t),\n\t\t\t\tName:       \"John Doe\",\n\t\t\t\tGivenName:  \"John\",\n\t\t\t\tFamilyName: \"Doe\",\n\t\t\t\tPicture:    \"https://example.com/avatar.png\",\n\t\t\t},\n\t\t\texpectedClaims: json.RawMessage(`{\n\t\t\t\t\"user_id\": \"48986f51-d9c8-4f22-89e9-fb7fab959399\",\n\t\t\t\t\"username\": \"test_user\",\n\t\t\t\t\"email\": {\n\t\t\t\t\t\"address\": \"test@example.com\",\n\t\t\t\t\t\"is_verified\": true,\n\t\t\t\t\t\"is_primary\": true\n\t\t\t\t},\n\t\t\t\t\"metadata\": {\n\t\t\t\t\t\"public_metadata\": {\n\t\t\t\t\t\t\"public_key\": \"public_value\"\n\t\t\t\t\t},\n\t\t\t\t\t\"unsafe_metadata\": {\n\t\t\t\t\t\t\"unsafe_key\": \"unsafe_value\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"full_name\": \"John Doe\",\n\t\t\t\t\"first_name\": \"John\",\n\t\t\t\t\"last_name\": \"Doe\",\n\t\t\t\t\"avatar\": \"https://example.com/avatar.png\"\n\t\t\t}`),\n\t\t},\n\t\t{\n\t\t\tname: \"should process more complex go templates\",\n\t\t\tclaims: map[string]interface{}{\n\t\t\t\t\"greeting\":                     \"Hello {{ .User.Email.Address }}\",\n\t\t\t\t\"greeting_pipelined\":           \"Hello {{ .User.Email.Address | printf \\\"%s\\\" }}\",\n\t\t\t\t\"verification_msg_conditional\": \"{{if .User.Email.IsVerified}}Verified{{else}}Unverified{{end}} user {{.User.Email.Address}}\",\n\t\t\t\t\"verification_msg_conditional_pretty\": `\n\t\t\t\t\t{{- if .User.Email.IsVerified -}}\n\t\t\t\t\t\tVerified\n\t\t\t\t\t{{- else -}}\n\t\t\t\t\t\tUnverified\n\t\t\t\t\t{{ end }} user {{ .User.Email.Address }}`,\n\t\t\t},\n\t\t\tuser: dto.UserJWT{\n\t\t\t\tEmail: &dto.EmailJWT{\n\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\tIsVerified: true,\n\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t},\n\t\t\t\tUsername: \"test_user\",\n\t\t\t\tUserID:   \"48986f51-d9c8-4f22-89e9-fb7fab959399\",\n\t\t\t},\n\t\t\texpectedClaims: json.RawMessage(`{\n\t\t\t\t\"greeting\": \"Hello test@example.com\",\n\t\t\t\t\"greeting_pipelined\": \"Hello test@example.com\",\n\t\t\t\t\"verification_msg_conditional\": \"Verified user test@example.com\",\n\t\t\t\t\"verification_msg_conditional_pretty\": \"Verified user test@example.com\"\n\t\t\t}`),\n\t\t},\n\t\t{\n\t\t\tname: \"should ignore entries with invalid templates or processing errors\",\n\t\t\tclaims: map[string]interface{}{\n\t\t\t\t\"valid_template\":                        \"Hello {{ .User.Username }}\",\n\t\t\t\t\"invalid_template\":                      \"Hello {{ .User.Username }\",\n\t\t\t\t\"non_existing_field_on_context_data\":    \"Hello {{ .User.Surname }}\",\n\t\t\t\t\"non_existing_function_on_context_data\": `Hello {{ .User.Metadata.Private \"private_key\" }}`,\n\t\t\t},\n\t\t\tuser: dto.UserJWT{\n\t\t\t\tEmail: &dto.EmailJWT{\n\t\t\t\t\tAddress:    \"test@example.com\",\n\t\t\t\t\tIsVerified: true,\n\t\t\t\t\tIsPrimary:  true,\n\t\t\t\t},\n\t\t\t\tUsername: \"test_user\",\n\t\t\t\tUserID:   \"48986f51-d9c8-4f22-89e9-fb7fab959399\",\n\t\t\t\tMetadata: dto.NewMetadataJWT(\n\t\t\t\t\tjson.RawMessage(`{\"public_key\": \"public_value\"}`),\n\t\t\t\t\tjson.RawMessage(`{\"unsafe_key\": \"unsafe_value\"}`),\n\t\t\t\t),\n\t\t\t},\n\t\t\texpectedClaims: json.RawMessage(`{\n\t\t\t\t\"valid_template\": \"Hello test_user\"\n\t\t\t}`),\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"should process access to metadata\",\n\t\t\tclaims: map[string]interface{}{\n\t\t\t\t\"display_name\":                          `{{ .User.Metadata.Public \"display_name\" }}`,\n\t\t\t\t\"favorite_games\":                        `{{ .User.Metadata.Public \"favorite_games\" }}`,\n\t\t\t\t\"favorite_games_with_playtime_over_100\": `{{ .User.Metadata.Public \"favorite_games.#(playtime_hours>100)\" }}`,\n\t\t\t\t\"favorite_genres\":                       `{{ .User.Metadata.Public \"favorite_games.#.genre\"}}`,\n\t\t\t\t\"ui_theme\":                              `{{ .User.Metadata.Unsafe \"ui_theme\" }}`,\n\t\t\t},\n\t\t\tuser: dto.UserJWT{\n\t\t\t\tMetadata: dto.NewMetadataJWT(\n\t\t\t\t\tjson.RawMessage(`{\n\t\t\t\t\t\t\"display_name\": \"GamerDude\",\n\t\t\t\t\t\t\"favorite_games\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"Legends of Valor\",\n\t\t\t\t\t\t\t\t\"genre\": \"RPG\",\n\t\t\t\t\t\t\t\t\"playtime_hours\": 142.3\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"Space Raiders\",\n\t\t\t\t\t\t\t\t\"genre\": \"Sci-Fi Shooter\",\n\t\t\t\t\t\t\t\t\"playtime_hours\": 87.6\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t]\n\t\t\t\t\t}`),\n\t\t\t\t\tjson.RawMessage(`{\n\t\t\t\t\t\t\"ui_theme\": \"dark\"\n\t\t\t\t\t}`),\n\t\t\t\t),\n\t\t\t},\n\t\t\texpectedClaims: json.RawMessage(`{\n\t\t\t\t\"display_name\": \"GamerDude\",\n\t\t\t\t\"favorite_games\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"Legends of Valor\",\n\t\t\t\t\t\t\"genre\": \"RPG\",\n\t\t\t\t\t\t\"playtime_hours\": 142.3\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"Space Raiders\",\n\t\t\t\t\t\t\"genre\": \"Sci-Fi Shooter\",\n\t\t\t\t\t\t\"playtime_hours\": 87.6\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"favorite_games_with_playtime_over_100\": {\n\t\t\t\t\t\"name\": \"Legends of Valor\",\n\t\t\t\t\t\"genre\": \"RPG\",\n\t\t\t\t\t\"playtime_hours\": 142.3\n\t\t\t\t},\n\t\t\t\t\"favorite_genres\": [\"RPG\", \"Sci-Fi Shooter\"],\n\t\t\t\t\"ui_theme\": \"dark\"\n\t\t\t}`),\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\ttoken := jwt.New()\n\t\t\terr := ProcessJWTTemplate(token, tt.claims, tt.user)\n\t\t\tassert.NoError(t, err)\n\n\t\t\tprivateClaims := token.PrivateClaims()\n\t\t\tprivateClaimsBytes, err := json.Marshal(privateClaims)\n\t\t\tassert.NoError(t, err)\n\n\t\t\trequire.True(t, gjson.ValidBytes(tt.expectedClaims))\n\t\t\trequire.True(t, gjson.ValidBytes(privateClaimsBytes))\n\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\tgjson.GetBytes(tt.expectedClaims, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t\tgjson.GetBytes(privateClaimsBytes, `@pretty:{\"sortKeys\":true}`).String(),\n\t\t\t)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/template/template.go",
    "content": "package template\n\nimport (\n\t\"embed\"\n\t\"github.com/labstack/echo/v4\"\n\t\"html/template\"\n\t\"io\"\n)\n\n//go:embed templates/*\nvar templateFS embed.FS\n\ntype Template struct {\n\ttemplates *template.Template\n}\n\nfunc (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {\n\treturn t.templates.ExecuteTemplate(w, name, data)\n}\n\nfunc NewTemplateRenderer() *Template {\n\treturn &Template{\n\t\ttemplates: template.Must(template.ParseFS(templateFS, \"templates/*.tmpl\")),\n\t}\n}\n"
  },
  {
    "path": "backend/template/templates/status.tmpl",
    "content": "{{define \"status\"}}\n<!DOCTYPE html>\n<html>\n<head>\n  <title>API Integration Status</title>\n</head>\n<body>\n<h1>API Status</h1>\n{{if .dbError}}\n<p>❌ API is not functioning properly due to database connectivity problems.</p>\n{{else}}\n<p>✅ API is running.</p>\n<p>Check integration guides on <a href=\"https://docs.hanko.io\" target=\"_blank\">docs.hanko.io</a></p>\n{{end}}\n</body>\n</html>\n{{end}}\n"
  },
  {
    "path": "backend/test/audit_logger.go",
    "content": "package test\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n\nfunc NewAuditLogger() auditlog.Logger {\n\treturn &auditLogger{}\n}\n\ntype auditLogger struct {\n}\n\nfunc (a *auditLogger) Create(context echo.Context, logType models.AuditLogType, user *models.User, err error, opts ...auditlog.DetailOption) error {\n\treturn nil\n}\n\nfunc (a *auditLogger) CreateWithConnection(tx *pop.Connection, context echo.Context, logType models.AuditLogType, user *models.User, err error, opts ...auditlog.DetailOption) error {\n\treturn nil\n}\n"
  },
  {
    "path": "backend/test/config.go",
    "content": "package test\n\nimport \"github.com/teamhanko/hanko/backend/v2/config\"\n\nvar DefaultConfig = config.Config{\n\tWebauthn: config.WebauthnSettings{\n\t\tRelyingParty: config.RelyingParty{\n\t\t\tId:          \"localhost\",\n\t\t\tDisplayName: \"Test Relying Party\",\n\t\t\tIcon:        \"\",\n\t\t\tOrigins:     []string{\"http://localhost:8080\", \"http://localhost:8888\"},\n\t\t},\n\t\tTimeout:          60000,\n\t\tUserVerification: \"preferred\",\n\t},\n\tSecrets: config.Secrets{\n\t\tKeys: []string{\"abcdefghijklmnop\"},\n\t\tKeyManagement: config.KeyManagement{\n\t\t\tType: \"local\",\n\t\t},\n\t},\n\tEmail: config.Email{\n\t\tEnabled:              true,\n\t\tUseForAuthentication: true,\n\t},\n\tEmailDelivery: config.EmailDelivery{\n\t\tEnabled: true,\n\t\tSMTP: config.SMTP{\n\t\t\tHost: \"localhost\",\n\t\t\tPort: \"2500\",\n\t\t},\n\t\tFromAddress: \"test@hanko.io\",\n\t\tFromName:    \"Hanko Test\",\n\t},\n\tPasscode: config.Passcode{\n\t\tTTL: 300,\n\t},\n\tSession: config.Session{\n\t\tLifespan: \"1h\",\n\t\tCookie: config.Cookie{\n\t\t\tSameSite: \"none\",\n\t\t},\n\t\tLimit: 5,\n\t},\n\tService: config.Service{\n\t\tName: \"Test\",\n\t},\n\tAccount: config.Account{\n\t\tAllowSignup:   true,\n\t\tAllowDeletion: false,\n\t},\n\tPasskey: config.Passkey{\n\t\tEnabled:          true,\n\t\tUserVerification: \"preferred\",\n\t},\n}\n"
  },
  {
    "path": "backend/test/database.go",
    "content": "package test\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t_ \"github.com/go-sql-driver/mysql\"\n\t_ \"github.com/lib/pq\"\n\t\"github.com/ory/dockertest/v3\"\n\t\"github.com/ory/dockertest/v3/docker\"\n\t\"time\"\n)\n\nvar database_user = \"hanko\"\nvar database_password = \"hanko\"\nvar database_name = \"hanko_test\"\n\ntype TestDB struct {\n\tpool        *dockertest.Pool\n\tresource    *dockertest.Resource\n\tDatabaseUrl string\n\tDbCon       *sql.DB\n\tDialect     string\n}\n\n// StartDB starts a database in a docker container with the specified dialect and name.\n// The name is used to name the container, so that multiple container can be started in parallel.\nfunc StartDB(name string, dialect string) (*TestDB, error) {\n\t// uses a sensible default on windows (tcp/http) and linux/osx (socket)\n\tpool, err := dockertest.NewPool(\"\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not construct pool: %w\", err)\n\t}\n\n\terr = pool.Client.Ping()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not connect to docker: %w\", err)\n\t}\n\n\toptions, err := getContainerOptions(dialect)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not create docker run options: %w\", err)\n\t}\n\n\toptions.Name = name\n\n\t// pulls an image, creates a container based on it and runs it\n\tresource, err := pool.RunWithOptions(options, func(config *docker.HostConfig) {\n\t\t// set AutoRemove to true so that stopped container goes away by itself\n\t\tconfig.AutoRemove = true\n\t\tconfig.RestartPolicy = docker.RestartPolicy{Name: \"no\"}\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not start resource: %w\", err)\n\t}\n\n\thostAndPort := resource.GetHostPort(getPortID(dialect))\n\tdsn := getDsn(dialect, hostAndPort)\n\n\t_ = resource.Expire(300)\n\n\tpool.MaxWait = 120 * time.Second\n\tif err = pool.Retry(func() error {\n\t\tdb, err := sql.Open(dialect, dsn)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn db.Ping()\n\t}); err != nil {\n\t\treturn nil, fmt.Errorf(\"could not connect to docker: %w\", err)\n\t}\n\n\tdb, err := sql.Open(dialect, dsn)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not connect to database: %w\", err)\n\t}\n\n\tdbUrl := \"\"\n\tswitch dialect {\n\tcase \"mysql\":\n\t\tdbUrl = fmt.Sprintf(\"mysql://%s\", dsn)\n\tdefault:\n\t\tdbUrl = dsn\n\t}\n\n\treturn &TestDB{\n\t\tpool:        pool,\n\t\tresource:    resource,\n\t\tDatabaseUrl: dbUrl,\n\t\tDbCon:       db,\n\t\tDialect:     dialect,\n\t}, nil\n}\n\n// PurgeDB stops the docker container.\nfunc PurgeDB(db *TestDB) error {\n\tif db == nil {\n\t\treturn nil\n\t}\n\tif err := db.pool.Purge(db.resource); err != nil {\n\t\treturn fmt.Errorf(\"could not purge resource: %w\", err)\n\t}\n\treturn nil\n}\n\n// getContainerOptions returns the options to start a container, which includes the docker image, tag, env variables, ...\nfunc getContainerOptions(dialect string) (*dockertest.RunOptions, error) {\n\tswitch dialect {\n\tcase \"postgres\":\n\t\treturn &dockertest.RunOptions{\n\t\t\tRepository: \"postgres\",\n\t\t\tTag:        \"12-alpine\",\n\t\t\tEnv: []string{\n\t\t\t\tfmt.Sprintf(\"POSTGRES_PASSWORD=%s\", database_password),\n\t\t\t\tfmt.Sprintf(\"POSTGRES_USER=%s\", database_user),\n\t\t\t\tfmt.Sprintf(\"POSTGRES_DB=%s\", database_name),\n\t\t\t\t\"listen_addresses = '*'\",\n\t\t\t},\n\t\t}, nil\n\tcase \"mysql\":\n\t\treturn &dockertest.RunOptions{\n\t\t\tRepository: \"mysql\",\n\t\t\tTag:        \"8\",\n\t\t\tEnv: []string{\n\t\t\t\tfmt.Sprintf(\"MYSQL_USER=%s\", database_user),\n\t\t\t\tfmt.Sprintf(\"MYSQL_PASSWORD=%s\", database_password),\n\t\t\t\tfmt.Sprintf(\"MYSQL_DATABASE=%s\", database_name),\n\t\t\t\t\"MYSQL_RANDOM_ROOT_PASSWORD=true\",\n\t\t\t},\n\t\t}, nil\n\tdefault:\n\t\treturn nil, UnknownDialectError\n\t}\n}\n\nvar UnknownDialectError = errors.New(\"unknown dialect\")\n\nfunc getPortID(dialect string) string {\n\tswitch dialect {\n\tcase \"postgres\":\n\t\treturn \"5432/tcp\"\n\tcase \"mysql\":\n\t\treturn \"3306/tcp\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc getDsn(dialect string, hostAndPort string) string {\n\tswitch dialect {\n\tcase \"postgres\":\n\t\treturn fmt.Sprintf(\"postgres://%s:%s@%s/%s?sslmode=disable\", database_user, database_password, hostAndPort, database_name)\n\tcase \"mysql\":\n\t\treturn fmt.Sprintf(\"%s:%s@(%s)/%s?parseTime=true&multiStatements=true&readTimeout=5s&collation=utf8mb4_general_ci\", database_user, database_password, hostAndPort, database_name)\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "backend/test/fixtures/actions/get_wa_creation_options/flows.yaml",
    "content": "- id: 0b41f4dd-8e46-4a7c-bb4d-d60843113431\n  current_state: onboarding_create_passkey\n  stash_data: \"{\\\"webauthn_available\\\":\\\"true\\\",\\\"email\\\":\\\"example@example.com\\\",\\\"username\\\":\\\"john.doe\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: de87cfc6-a6e2-434d-bbe8-5e5004c9deda\n  current_state: onboarding_create_passkey\n  stash_data: \"{\\\"webauthn_available\\\":\\\"true\\\",\\\"username\\\":\\\"john.doe\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: a77e23b2-7ca5-4c76-a20b-c17b7dbcb117\n  current_state: onboarding_create_passkey\n  stash_data: \"{\\\"webauthn_available\\\":\\\"true\\\",\\\"email\\\":\\\"example@example.com\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: be57518c-6bd5-4b3e-a91a-6c082e212a58\n  current_state: onboarding_create_passkey\n  stash_data: \"{\\\"webauthn_available\\\":\\\"false\\\",\\\"email\\\":\\\"example@example.com\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n"
  },
  {
    "path": "backend/test/fixtures/actions/send_capabilities/flows.yaml",
    "content": "- id: 0b41f4dd-8e46-4a7c-bb4d-d60843113431\n  current_state: registration_preflight\n  stash_data: {}\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n\n"
  },
  {
    "path": "backend/test/fixtures/actions/send_wa_attestation_response/flows.yaml",
    "content": "- id: 0b41f4dd-8e46-4a7c-bb4d-d60843113431\n  current_state: onboarding_verify_passkey_attestation\n  stash_data: \"{\\\"webauthn_available\\\":\\\"true\\\",\\\"webauthn_session_data_id\\\":\\\"adce0002-35bc-c60a-648b-0b25f1f05503\\\",\\\"user_id\\\":\\\"ec4ef049-5b88-4321-a173-21b0eff06a04\\\",\\\"email\\\":\\\"john.doe@example.com\\\",\\\"_\\\":{\\\"scheduled_states\\\":[\\\"success\\\"]}}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 53d35f35-c87d-4533-b966-2b48686b9be9\n  current_state: onboarding_verify_passkey_attestation\n  stash_data: \"{\\\"webauthn_available\\\":\\\"true\\\",\\\"webauthn_session_data_id\\\":\\\"65f13ce2-d118-44f0-a38b-8e3ee918c6f3\\\",\\\"user_id\\\":\\\"ec4ef049-5b88-4321-a173-21b0eff06a04\\\",\\\"email\\\":\\\"john.doe@example.com\\\",\\\"_\\\":{\\\"scheduled_states\\\":[\\\"success\\\"]}}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 4447f8df-713c-43c2-ae8d-ce2bc8e29cc7\n  current_state: onboarding_verify_passkey_attestation\n  stash_data: \"{\\\"webauthn_available\\\":\\\"false\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n"
  },
  {
    "path": "backend/test/fixtures/actions/send_wa_attestation_response/webauthn_session_data.yaml",
    "content": "- id: adce0002-35bc-c60a-648b-0b25f1f05503\n  challenge: \"tOrNDCD2xQf4zFjEjwxaP8fOErP3zz08rMoTlJGtnKU\"\n  user_id: \"ec4ef049-5b88-4321-a173-21b0eff06a04\"\n  user_verification: \"required\"\n  operation: \"registration\"\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n# expired session data\n- id: 65f13ce2-d118-44f0-a38b-8e3ee918c6f3\n  challenge: \"FeMc7sR9ElehwEU5TtEWFi7rPP3-kdZXgnwLtlb3ChY\"\n  user_id: \"ec4ef049-5b88-4321-a173-21b0eff06a04\"\n  user_verification: \"required\"\n  operation: \"registration\"\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  expires_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/actions/submit_new_password/flows.yaml",
    "content": "- id: 0b41f4dd-8e46-4a7c-bb4d-d60843113431\n  current_state: password_creation\n  stash_data: \"{\\\"webauthn_available\\\":true}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 8a2cf90d-dea5-4678-9dca-6707dab6af77\n  current_state: password_creation\n  stash_data: \"{\\\"webauthn_available\\\":false}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n"
  },
  {
    "path": "backend/test/fixtures/actions/submit_passcode/flows.yaml",
    "content": "- id: 0b41f4dd-8e46-4a7c-bb4d-d60843113431\n  current_state: registration_email_verification\n  stash_data: \"{\\\"passcode_id\\\":\\\"2b10fbae-0286-4719-81a6-b52262a02266\\\",\\\"webauthn_available\\\":true}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 8a2cf90d-dea5-4678-9dca-6707dab6af77\n  current_state: registration_email_verification\n  stash_data: \"{\\\"passcode_id\\\":\\\"927b5be2-0add-4d98-b0bb-40c8462b3cca\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 23524801-f445-4859-bc16-22cf1dd417ac\n  current_state: registration_email_verification\n  stash_data: \"{}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: fc4dc7e4-bce7-4154-873b-cb3d766df279\n  current_state: registration_email_verification\n  stash_data: \"{\\\"passcode_id\\\":\\\"e086ac7e-19df-4e92-9d4a-9b0a02934507\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 5a862a2d-0d10-4904-b297-cb32fc43c859\n  current_state: registration_email_verification\n  stash_data: \"{\\\"passcode_id\\\":\\\"9b09dcaf-1603-4708-b9e0-239725378576\\\"}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: bc3173e7-3204-4b9a-904b-9f812330b0de\n  current_state: registration_email_verification\n  stash_data: \"{\\\"passcode_id\\\":\\\"2b10fbae-0286-4719-81a6-b52262a02266\\\",\\\"webauthn_available\\\":false}\"\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n"
  },
  {
    "path": "backend/test/fixtures/actions/submit_passcode/passcodes.yaml",
    "content": "- id: 2b10fbae-0286-4719-81a6-b52262a02266\n  ttl: 300\n  code: $2a$12$pYlnZB/ornewcsD6EwuBPOj14bcc8SnZcgbz5nNuWebnVpMioaIVC\n  try_count: 0\n  created_at: 2025-01-01 00:00:00\n  updated_at: 2025-01-01 00:00:00\n  flow_id:  0b41f4dd-8e46-4a7c-bb4d-d60843113431\n- id: 927b5be2-0add-4d98-b0bb-40c8462b3cca\n  ttl: 300\n  code: $2a$12$pYlnZB/ornewcsD6EwuBPOj14bcc8SnZcgbz5nNuWebnVpMioaIVC\n  try_count: 3\n  created_at: 2025-01-01 00:00:00\n  updated_at: 2025-01-01 00:00:00\n  flow_id:  8a2cf90d-dea5-4678-9dca-6707dab6af77\n- id: 9b09dcaf-1603-4708-b9e0-239725378576\n  ttl: 300\n  code: $2a$12$pYlnZB/ornewcsD6EwuBPOj14bcc8SnZcgbz5nNuWebnVpMioaIVC\n  try_count: 3\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n  flow_id:  5a862a2d-0d10-4904-b297-cb32fc43c859\n"
  },
  {
    "path": "backend/test/fixtures/actions/submit_registration_identifier/emails.yaml",
    "content": "- id: 7c8a09a9-5418-4e01-9918-968e58982a3a\n  user_id: b7fe9f28-bc2d-44fc-819e-bcb478656b94\n  address: john.doe@example.com\n  verified: true\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: c23941fc-382f-46a3-bf0e-ff0f4258c58b\n  user_id: 0f813887-5479-42d8-b8d7-3e7a2f426516\n  address: jane.doe@example.com\n  verified: true\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n"
  },
  {
    "path": "backend/test/fixtures/actions/submit_registration_identifier/flows.yaml",
    "content": "- id: 0b41f4dd-8e46-4a7c-bb4d-d60843113431\n  current_state: registration_init\n  stash_data: {}\n  version: 0\n  expires_at: 2099-12-31 23:59:59\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n"
  },
  {
    "path": "backend/test/fixtures/actions/submit_registration_identifier/users.yaml",
    "content": "- id: b7fe9f28-bc2d-44fc-819e-bcb478656b94\n  username: john.doe\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 0f813887-5479-42d8-b8d7-3e7a2f426516\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n- id: 9ee61f69-952b-400a-adc9-2800aef967fa\n  username: max.mustermann\n  created_at: 2023-01-01 00:00:00\n  updated_at: 2023-01-01 00:00:00\n"
  },
  {
    "path": "backend/test/fixtures/email/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+1@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  user_id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  address: john.doe+2@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 8bb4c8a7-a3e6-48bb-b54f-20e3b485ab33\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+3@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+4@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 0394ad95-6ea3-4cbd-940b-3f6dd10440b0\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+5@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 5458fbb3-a58b-45df-ab06-937a3e1a140e\n  address: john.doe+6@example.com\n  verified: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 7c4473b8-ddcc-480b-b01f-df89e99f74c9\n  address: john.doe+7@example.com\n  verified: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/email/primary_emails.yaml",
    "content": "- id: 8fe72e5f-edb6-40e7-83a7-a7e858c2c62d\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 3a5f340f-07f7-40dc-a507-d5919915e11d\n  email_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/email/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: d41df4b7-c055-45e6-9faf-61aa92a4032e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/metadata/user_metadata.yaml",
    "content": "- id: 7c9e6679-7425-40de-944b-e07fc1f90ae7\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  public_metadata: {\"existing_public_str\":\"data\",\"existing_public_num\":1,\"existing_public_bool\":true}\n  private_metadata: {\"existing_private_str\":\"data\",\"existing_private_arr\":[\"existing_private_arr_0\",\"existing_private_arr_1\"]}\n  unsafe_metadata: {\"existing_unsafe_str\":\"data\",\"existing_unsafe_obj\":{\"existing_unsafe_obj_key\":\"existing_unsafe_obj_value\"}}\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/metadata/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59 "
  },
  {
    "path": "backend/test/fixtures/otp/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+1@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  user_id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  address: john.doe+2@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 8bb4c8a7-a3e6-48bb-b54f-20e3b485ab33\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+3@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+4@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 0394ad95-6ea3-4cbd-940b-3f6dd10440b0\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+5@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/otp/otp_secrets.yaml",
    "content": "- id: f28b15df-6e09-4ac0-b49f-e4e2d274f939\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  secret: RANDOMOTPSECRET\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/otp/primary_emails.yaml",
    "content": "- id: 8fe72e5f-edb6-40e7-83a7-a7e858c2c62d\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 3a5f340f-07f7-40dc-a507-d5919915e11d\n  email_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/otp/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: d41df4b7-c055-45e6-9faf-61aa92a4032e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/passcode/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+1@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  user_id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  address: john.doe+2@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 8bb4c8a7-a3e6-48bb-b54f-20e3b485ab33\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+3@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+4@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 0394ad95-6ea3-4cbd-940b-3f6dd10440b0\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+5@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 7c4473b8-ddcc-480b-b01f-df89e99f74c9\n  address: john.doe+6@example.com\n  verified: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/passcode/primary_emails.yaml",
    "content": "- id: 8fe72e5f-edb6-40e7-83a7-a7e858c2c62d\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 3a5f340f-07f7-40dc-a507-d5919915e11d\n  email_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/passcode/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: d41df4b7-c055-45e6-9faf-61aa92a4032e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/password/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+1@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  user_id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  address: john.doe+2@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 8bb4c8a7-a3e6-48bb-b54f-20e3b485ab33\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+3@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: f194ee0f-dd1a-48f7-8766-c67e4d6cd1fe\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe+4@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 0394ad95-6ea3-4cbd-940b-3f6dd10440b0\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+5@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/password/password_credentials.yaml",
    "content": "- id: 6a565180-2366-45b1-8785-39f7902c7f2e\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  password: $2a$12$Cf7k.dG6pznTUJ5u2u1pgu6I4VXH5.9O0NZsDk8TwWwyBkZovYVli\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/password/primary_emails.yaml",
    "content": "- id: 8fe72e5f-edb6-40e7-83a7-a7e858c2c62d\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 3a5f340f-07f7-40dc-a507-d5919915e11d\n  email_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/password/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: d41df4b7-c055-45e6-9faf-61aa92a4032e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/saml_state/saml_states.yaml",
    "content": "- id: 3986a791-fe8d-4622-8668-6bab85ecd58f\n  nonce: \"VSPVgv0ZWxnvZtVkh7imAu1KQRONFsUhGDxgp9x82aU=\"\n  state: \"HmD7wlGQ7bF_4MGtmFRQuuSGTshHETDs4RQa64JAx-6EsmNsUjaQwYNOnjWUs6qIOuQMBTKapDGVXVCk00pX2vSS-x-WVqdzZ8KyeQ-9IHu2mwb-AeRbb2QPE-GFnvp2wrbCskKvWvtOfipyeTsnYY5iM90DxssaUtvKnawaB5_MNNekfKyiOeepIkKjUfSJ6-yTR7AAA4B9jwOfDRB4zdV8kKPVJlGVBJFosL11YWJaLxRGQR69nah3Jf9Z6bSAGXxWp24PoBYhij-dH4JyDCcU7D-NeT2A8qFFFjQ1m28C8fsr6zqb4w==\"\n  expires_at: 2020-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id:        ad73031f-b45c-4b18-8d99-06eacde7eb98\n  nonce:     \"lorem\"\n  state:     \"invalid_state\"\n  expires_at: 2020-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 6f70ccf1-8018-423a-8163-7f0e83997c83\n  nonce:     \"BxFqBzDKttD_v-m-GxrTSXJbYKFVnn7eloTLzoyYwJ0=\"\n  state:     \"Fs1SEytL1YuZJvBCyfF3Iydpl3-aC95bDsIb3VIcbq3sZjhn6iQaNFVNQZG1Maz2bI7Zkz3UvEIrIFohIotDqoR057mBnDKHVeg-TZKLXfviPJqfkOldfNPTpJgO9e4biYGxPx6qkmabk83eO77Qe3rJ2XM6FznQqPoMjq1vOBBJXjBUPeu3KtF1l7ONpHHVCV1Sr0cm0qQ4q5nPYgjI227AOa_MOOIDKfwqxb-jQT_9n0vLtVen9aYFbr_i_57r5aC3nCxnBgq7gXDq7z-E5NDRW23E0x-5CpQdDhElmtoeu4XRx1jOfw==\"\n  expires_at: 2020-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: f00a3f26-36bd-4c50-ae98-59a728e7163a\n  nonce: \"AxFqBzDKttD_v-m-GxrTSXJbYKFVnn7eloTLzoyYwJ0=\"\n  state: \"invalid_state\"\n  expires_at: 2030-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 9a08fde5-e485-42c0-9f3b-112605280ac2\n  nonce: \"lmBPwLelStCMc--HmSVb-VaEDIHXVSY0d81EHMpI9nc=\"\n  state: \"UHPShpOq0byNI-A1vvUROLsPjNhGF5Xxdme4llZrnCvXfyZP_RnhRq490XPqKGKeWa621MtAUsV7N6C4OGx-EXK1TaLWNIYfFmByIEIlPSkpMVTFEUedY5UaFXwhWhuQb4Ci0r3QlPCLmQnPin1O4Vb59K2KieJwTvVnZzY3Y3Avj-D91acTMRGN6OabuIDDOH4nl0qDJABkZ6tnYk735ot8s3oyvJmZgmsW0qLOC_OoNMGZqLUTQzCrEayJ-gTXKr6HSvClN-4KGi4htHHLwYjrSCr_6tnQxDhZvNq7GKgXUEXfeUmZGQ==\"\n  expires_at: 2020-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/sessions/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: jane.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 1215f04b-0fad-4c54-899e-a4f7230fcc63\n  user_id: 46626836-f2db-4ec0-8752-858b544cbc78\n  address: test@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/sessions/primary_emails.yaml",
    "content": "- id: 8eaaa61b-ad65-45ac-a5b8-d7c6d301d29e\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: df8b4c07-f97e-48aa-8895-6c8e54d5b749\n  email_id: 1215f04b-0fad-4c54-899e-a4f7230fcc63\n  user_id: 46626836-f2db-4ec0-8752-858b544cbc78\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/sessions/sessions.yaml",
    "content": "- id: d8d6dc27-fcf9-4a5c-bb50-a7a03067d936\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  user_agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\n  ip_address: 192.168.0.1\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  expires_at: 2100-12-31 23:59:59\n  last_used: 2020-12-31 23:59:59\n- id: 74ba812a-923a-43e4-8020-9535dcadc0a8\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  user_agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\n  ip_address: 192.168.0.1\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  expires_at: 2100-12-31 23:59:59\n  last_used: 2020-12-31 23:59:59\n- id: 108f3789-a795-43bd-a58f-ac8e80a213cd\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\n  ip_address: 192.168.0.1\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  expires_at: 2100-12-31 23:59:59\n  last_used: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/sessions/users.yaml",
    "content": "- id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 46626836-f2db-4ec0-8752-858b544cbc78\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/thirdparty/emails.yaml",
    "content": "- id: 15ed767e-1544-4e03-a732-4bf80caa2e78\n  user_id: e45a7f3f-029d-46fc-a2c6-bed892b1e84e\n  address: test-with-google-identity@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# satisfies the scenario, where a user changes her primary provider email address changes to another existing email\n# address for that user (identity should then be coupled to this email address)\n- id: 3d9d99d8-ab27-43db-a6ca-e524974de4f0\n  user_id: e45a7f3f-029d-46fc-a2c6-bed892b1e84e\n  address: test-with-google-identity-changed@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 80939e4c-0028-4b67-aeee-98c617a20b6b\n  user_id: 1d6a4824-d935-4980-8f18-8ee5d6efe2fc\n  address: test-with-github-identity@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 05ab6e1f-8dfb-4329-ae04-22571a68d96b\n  user_id: b3537e49-de92-4e16-8981-ae4beb44c447\n  address: test-with-apple-identity@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 09f35529-cca6-44a7-ab1d-b07e95a04e3b\n  user_id: d69bffda-4e4a-4424-a238-fbecc1651d81\n  address: test-with-discord-identity@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: d781006b-4f55-4327-bad6-55bc34b88585\n  user_id: 48df412f-a7b1-4fbc-ad2d-56bd3e103fd7\n  address: test-with-microsoft-identity@example.com\n  verified: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 967ce4a0-677d-4dc3-bacf-53d54471369c\n  user_id:\n  address: test-with-facebook-identity@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 527afce8-3b7b-41b6-b1ed-33d408c5a7bb\n  user_id: 43fb7e88-4d5d-4b2b-9335-391e78d7e472\n  address: test-no-identity@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 490c7b84-d62f-465b-8471-75dac538e4f3\n  user_id:\n  address: unclaimed-email@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 982cc627-c56c-415d-aa49-fcaa1b39cf14\n  user_id: 5409b292-c1be-421a-ac7d-f8ece1dcd4e4\n  address: provider-primary-email-changed@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/thirdparty/identities.yaml",
    "content": "- id: 443a984d-bb1c-46fe-b685-151bd0f017b1\n  provider_user_id: \"google_abcde\"\n  provider_id: \"google\"\n  data: '{\"email\":\"test-with-google-identity@example.com\",\"email_verified\":true,\"iss\":\"https://www.googleapis.com\",\"sub\":\"google_abcde\"}'\n  email_id: 15ed767e-1544-4e03-a732-4bf80caa2e78\n  user_id: e45a7f3f-029d-46fc-a2c6-bed892b1e84e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: b140230b-be5b-4589-9762-ec72f32d2833\n  provider_user_id: \"1234\"\n  provider_id: \"github\"\n  data: '{\"email\":\"test-with-github-identity@example.com\",\"email_verified\":true,\"iss\":\"https://api.github.com\",\"sub\":\"1234\"}'\n  email_id: 80939e4c-0028-4b67-aeee-98c617a20b6b\n  user_id: 1d6a4824-d935-4980-8f18-8ee5d6efe2fc\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 09b084b7-36af-41c5-b4b6-db9b8ed8385b\n  provider_user_id: \"apple_abcde\"\n  provider_id: \"apple\"\n  data: '{\"email\":\"test-with-apple-identity@example.com\",\"email_verified\":true,\"iss\":\"https://appleid.apple.com\",\"sub\":\"apple_abcde\"}'\n  email_id: 05ab6e1f-8dfb-4329-ae04-22571a68d96b\n  user_id: b3537e49-de92-4e16-8981-ae4beb44c447\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 18d61f13-2789-467a-a3a6-4292c0621580\n  provider_user_id: \"discord_abcde\"\n  provider_id: \"discord\"\n  data: '{\"email\":\"test-with-discord-identity@example.com\",\"email_verified\":true,\"iss\":\"https://discord.com/api\",\"sub\":\"discord_abcde\"}'\n  email_id: 09f35529-cca6-44a7-ab1d-b07e95a04e3b\n  user_id: d69bffda-4e4a-4424-a238-fbecc1651d81\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e4cd954b-8f1f-4fa2-b7e9-60a0ffa56363\n  provider_user_id: \"microsoft_abcde\"\n  provider_id: \"microsoft\"\n  data: '{\"email\":\"test-with-microsoft-identity@example.com\",\"iss\":\"https://login.microsoftonline.com/common\",\"sub\":\"microsoft_abcde\"}'\n  email_id: d781006b-4f55-4327-bad6-55bc34b88585\n  user_id: 48df412f-a7b1-4fbc-ad2d-56bd3e103fd7\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: b6b1309d-61de-4a82-b8b8-d54db0be679b\n  provider_user_id: \"facebook_abcde\"\n  provider_id: \"facebook\"\n  data: '{\"email\":\"test-with-facebook-identity@example.com\",\"sub\":\"facebook_abcde\"}'\n  email_id: d781006b-4f55-4327-bad6-55bc34b88585\n  user_id: 48df412f-a7b1-4fbc-ad2d-56bd3e103fd7\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/thirdparty/primary_emails.yaml",
    "content": "- id: 8eaaa61b-ad65-45ac-a5b8-d7c6d301d29e\n  email_id: 15ed767e-1544-4e03-a732-4bf80caa2e78\n  user_id: e45a7f3f-029d-46fc-a2c6-bed892b1e84e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 08026479-0996-4bc0-8a3c-4692ca820075\n  email_id: 80939e4c-0028-4b67-aeee-98c617a20b6b\n  user_id: 1d6a4824-d935-4980-8f18-8ee5d6efe2fc\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: b2535c22-5dcb-4b65-a0d6-e8fd4a2e785d\n  email_id: 05ab6e1f-8dfb-4329-ae04-22571a68d96b\n  user_id: b3537e49-de92-4e16-8981-ae4beb44c447\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: f2eb0190-77f8-42a4-b45f-6f932a98995c\n  email_id: 09f35529-cca6-44a7-ab1d-b07e95a04e3b\n  user_id: d69bffda-4e4a-4424-a238-fbecc1651d81\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: fac1df9d-73da-44e6-97b7-eaf89ff10a25\n  email_id: d781006b-4f55-4327-bad6-55bc34b88585\n  user_id: 48df412f-a7b1-4fbc-ad2d-56bd3e103fd7\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: c57715eb-0c63-4910-b429-9b6165c50fab\n  email_id: 527afce8-3b7b-41b6-b1ed-33d408c5a7bb\n  user_id: 43fb7e88-4d5d-4b2b-9335-391e78d7e472\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e2beaaa9-1275-4eb5-aa28-9970b36d249e\n  email_id: 967ce4a0-677d-4dc3-bacf-53d54471369c\n  user_id: ef0a05a7-98d1-4e5a-a60f-2c5f740cd26d\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/thirdparty/users.yaml",
    "content": "# user with email and google identity\n- id: e45a7f3f-029d-46fc-a2c6-bed892b1e84e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# user with email and github identity\n- id: 1d6a4824-d935-4980-8f18-8ee5d6efe2fc\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# user with email and apple identity\n- id: b3537e49-de92-4e16-8981-ae4beb44c447\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# user with email and discord identity\n- id: d69bffda-4e4a-4424-a238-fbecc1651d81\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# user with email and microsoft identity\n- id: 48df412f-a7b1-4fbc-ad2d-56bd3e103fd7\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# user with email and facebook identity\n- id: ef0a05a7-98d1-4e5a-a60f-2c5f740cd26d\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# user with email, no identity\n- id: 43fb7e88-4d5d-4b2b-9335-391e78d7e472\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n# user with no identity, email equal to an email of a user WITH an identity\n# after that user has changed his primary provider to this email (\"multiple\n# accounts\" error scenario)\n- id: 5409b292-c1be-421a-ac7d-f8ece1dcd4e4\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/token/tokens.yaml",
    "content": "# Expired token\n- id: c125c6b5-37bf-446b-a1c7-6ee24eecd63e\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2022-03-20T15:13:10.168902Z\n  updated_at: 2022-03-20T15:12:01.639187Z\n  expires_at: 2022-03-20T15:18:10.168902Z\n  value: \"Trkauhl3q7XVxw5JcDH80lTe1KxzydIw0OcizH7umWk=\"\n  user_created: false\n  is_flow: false\n"
  },
  {
    "path": "backend/test/fixtures/token/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/user/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/user/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/user_admin/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: john.doe+1@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  user_id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  address: john.doe+2@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/user_admin/usernames.yaml",
    "content": "- id: 8c77f40a-4671-4246-a5af-06cb94ff888b\n  user_id: 6be8e0e7-05e6-4223-807b-06fe1d5e7b75\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  username: foouser\n"
  },
  {
    "path": "backend/test/fixtures/user_admin/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e0282f3f-b211-4f0e-b777-6fabc69287c9\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: d41df4b7-c055-45e6-9faf-61aa92a4032e\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 6be8e0e7-05e6-4223-807b-06fe1d5e7b75\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  picture: https://example.com/user1.jpg\n  name: \"Foo User\"\n  given_name: \"Foo\"\n  family_name: \"User\"\n- id: b3210fd8-c71f-4c9d-8123-a58f5f46a07f\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  picture: https://example.com/user1.jpg\n  name: \"Bar User\"\n  given_name: \"Bar\"\n  family_name: \"User\"\n"
  },
  {
    "path": "backend/test/fixtures/user_with_webauthn_credential/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/user_with_webauthn_credential/identities.yaml",
    "content": "- id: e4cd954b-8f1f-4fa2-b7e9-60a0ffa56363\n  provider_user_id: google_123\n  provider_id: google\n  data: '{\"email\":\"john.doe@example.com\",\"email_verified\":true,\"iss\":\"https://www.googleapis.com\",\"sub\":\"google_123\"}'\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/user_with_webauthn_credential/user_metadata.yaml",
    "content": "- id: 7c9e6679-7425-40de-944b-e07fc1f90ae7\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  public_metadata: {\"role\":\"tester\",\"tier\":\"gold\"}\n  private_metadata: {\"quota\": 42}\n  unsafe_metadata: {\"debug\": true}\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/user_with_webauthn_credential/usernames.yaml",
    "content": "- id: 8c77f40a-4671-4246-a5af-06cb94ff888b\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  username: johndoe\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/user_with_webauthn_credential/users.yaml",
    "content": "- id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  name: John Doe\n  given_name: John\n  family_name: Doe\n  picture: https://example.com/john.jpg\n"
  },
  {
    "path": "backend/test/fixtures/user_with_webauthn_credential/webauthn_credentials.yaml",
    "content": "- id: P8fcQ6U8zxJRzhI0yuUCOxcA_UyAs0jbauO5ektj4SM\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  public_key: public_key\n  attestation_type: none\n  aaguid: 00000000-0000-0000-0000-000000000000\n  sign_count: 0\n  backup_eligible: false\n  backup_state: false\n  mfa_only: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: security-key-cred-id\n  user_id: b5dd5267-b462-48be-b70d-bcd6f1bbe7a5\n  public_key: public_key_sk\n  attestation_type: none\n  aaguid: 00000000-0000-0000-0000-000000000000\n  sign_count: 2\n  backup_eligible: false\n  backup_state: false\n  mfa_only: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/webauthn/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: jane.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 1215f04b-0fad-4c54-899e-a4f7230fcc63\n  user_id: 46626836-f2db-4ec0-8752-858b544cbc78\n  address: test@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webauthn/primary_emails.yaml",
    "content": "- id: 8eaaa61b-ad65-45ac-a5b8-d7c6d301d29e\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: df8b4c07-f97e-48aa-8895-6c8e54d5b749\n  email_id: 1215f04b-0fad-4c54-899e-a4f7230fcc63\n  user_id: 46626836-f2db-4ec0-8752-858b544cbc78\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webauthn/users.yaml",
    "content": "- id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 46626836-f2db-4ec0-8752-858b544cbc78\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webauthn/webauthn_credentials.yaml",
    "content": "- id: AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjH\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  public_key: pQECAyYgASFYIPG9WtGAri-mevonFPH4p-lI3JBS29zjuvKvJmaP4_mRIlggOjHw31sdAGvE35vmRep-aPcbAAlbuc0KHxQ9u6zcHog\n  attestation_type: none\n  aaguid: adce0002-35bc-c60a-648b-0b25f1f05503\n  sign_count: 1650958750\n  backup_eligible: false\n  backup_state: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjK\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  public_key: pQECAyYgASFYIPG9WtGAri-mevonFPH4p-lI3JBS29zjuvKvJmaP4_mRIlggOjHw31sdAGvE35vmRep-aPcbAAlbuc0KHxQ9u6zcHoj\n  attestation_type: none\n  aaguid: adce0002-35bc-c60a-648b-0b25f1f05503\n  sign_count: 1650958750\n  backup_eligible: false\n  backup_state: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 4iVZGFN_jktXJmwmBmaSq0Qr4T62T0jX7PS7XcgAWlM\n  user_id: 46626836-f2db-4ec0-8752-858b544cbc78\n  public_key: pQECAyYgASFYIAeA_nt5TQ8c7bc8hN9_3zqzp3coXO5aplEeHMOQG0hrIlggf_KVxZI_nIedc1XMrwwOMaYNd0qxVpFK7vU79fGBoxY\n  attestation_type: none\n  aaguid: 01020304-0506-0708-0102-030405060708\n  sign_count: 1650958750\n  backup_eligible: false\n  backup_state: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webauthn/webauthn_session_data.yaml",
    "content": "- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  challenge: \"gKJKmh90vOpYO55oHpqaHX_oMCq4oTZt-D0b6teIzrE\"\n  user_id: \"00000000-0000-0000-0000-000000000000\"\n  user_verification: \"required\"\n  operation: \"authentication\"\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n# expired session data, explicitly set user_id because session data expiry is only checked for non-discoverable login\n- id: afcfeb0f-44e8-4d44-9fab-088459d228f5\n  challenge: \"AUgNkV8nmVwv-rl8-aK1ZEX5FlenYCsaTY8vdEcJKJQ\"\n  user_id: \"46626836-f2db-4ec0-8752-858b544cbc78\"\n  user_verification: \"required\"\n  operation: \"authentication\"\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  expires_at: 2020-12-31 23:59:59\n\n\n"
  },
  {
    "path": "backend/test/fixtures/webauthn_registration/emails.yaml",
    "content": "- id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  address: john.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  user_id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  address: jane.doe@example.com\n  verified: true\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webauthn_registration/primary_emails.yaml",
    "content": "- id: 8eaaa61b-ad65-45ac-a5b8-d7c6d301d29e\n  email_id: 51b7c175-ceb6-45ba-aae6-0092221c1b84\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webauthn_registration/users.yaml",
    "content": "- id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 38bf5a00-d7ea-40a5-a5de-48722c148925\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webauthn_registration/webauthn_credentials.yaml",
    "content": "- id: AaFdkcD4SuPjF-jwUoRwH8-ZHuY5RW46fsZmEvBX6RNKHaGtVzpATs06KQVheIOjYz-YneG4cmQOedzl0e0jF951ukx17Hl9jeGgWz5_DKZCO12p2-2LlzjK\n  user_id: ec4ef049-5b88-4321-a173-21b0eff06a04\n  public_key: pQECAyYgASFYIPG9WtGAri-mevonFPH4p-lI3JBS29zjuvKvJmaP4_mRIlggOjHw31sdAGvE35vmRep-aPcbAAlbuc0KHxQ9u6zcHoj\n  attestation_type: none\n  aaguid: adce0002-35bc-c60a-648b-0b25f1f05503\n  sign_count: 1650958750\n  backup_eligible: false\n  backup_state: false\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n"
  },
  {
    "path": "backend/test/fixtures/webauthn_registration/webauthn_session_data.yaml",
    "content": "- id: adce0002-35bc-c60a-648b-0b25f1f05503\n  challenge: \"tOrNDCD2xQf4zFjEjwxaP8fOErP3zz08rMoTlJGtnKU\"\n  user_id: \"ec4ef049-5b88-4321-a173-21b0eff06a04\"\n  user_verification: \"required\"\n  operation: \"registration\"\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n\n# expired session data\n- id: 65f13ce2-d118-44f0-a38b-8e3ee918c6f3\n  challenge: \"FeMc7sR9ElehwEU5TtEWFi7rPP3-kdZXgnwLtlb3ChY\"\n  user_id: \"ec4ef049-5b88-4321-a173-21b0eff06a04\"\n  user_verification: \"required\"\n  operation: \"registration\"\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n  expires_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webhooks/webhook_events.yaml",
    "content": "- id: 183ab00a-87a6-4356-b489-fd0badece0e3\n  webhook_id: a47fe92a-1e4b-4119-8653-55ad82737c88\n  event: user.create\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 51a3be04-c99d-40f3-a572-c76cadcb883d\n  webhook_id: a47fe92a-1e4b-4119-8653-55ad82737c88\n  event: user.update\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 3a4b1099-ec7b-4723-bee9-923d4faed27e\n  webhook_id: a47fe92a-1e4b-4119-8653-55ad82737c88\n  event: user\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: c4e6d7b6-34b3-4c9e-a259-814312162b2b\n  webhook_id: a47fe92a-1e4b-4119-8653-55ad82737c88\n  event: user.update.email\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: e3a1f35b-5578-43a5-9f62-63eb41c2d394\n  webhook_id: 8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\n  event: user\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/fixtures/webhooks/webhooks.yaml",
    "content": "- id: a47fe92a-1e4b-4119-8653-55ad82737c88\n  callback: http://lorem\n  enabled: 1\n  failures: 0\n  expires_at: 2220-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 279beae1-8a6d-4eaf-a791-1fa79d21d37a\n  callback: http://ipsum\n  enabled: 0\n  failures: 6\n  expires_at: 2220-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4\n  callback: http://localhost\n  enabled: 0\n  failures: 3\n  expires_at: 2220-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 8b00da9a-cacf-45ea-b25d-c1ce0f0d7da3\n  callback: http://localhost\n  enabled: 1\n  failures: 0\n  expires_at: 2020-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n- id: 8b00da9a-cacf-45ea-b25d-c1ce0f0d7da2\n  callback: http://localhost\n  enabled: 1\n  failures: 5\n  expires_at: 2020-12-31 23:59:59\n  created_at: 2020-12-31 23:59:59\n  updated_at: 2020-12-31 23:59:59\n"
  },
  {
    "path": "backend/test/jwk_manager.go",
    "content": "package test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n)\n\nvar privateKey = `{\n  \"alg\": \"RS256\",\n  \"d\": \"hsVXyJ1VjNFjiRqLE6bNZrAJDlnE33ptT4XpbPylfhlLfLB_OOB_YC5e4cBBoXlWaIJzYQ-qX0eSD2OdNg1JC0TgyQvwOqc9y6EKGyGu2asyHsJxLy8IiaqoqdqgiV0N_DsCYzt5Ew2nJq1P3XYqO5TJBpISixO47BEHaBgQeQBwSfmV3hmGYYTzJz6bwDNDhrBtg-2WiTfaq-3trorxo5Ww17-icr69Yad47Y4EIjKNL8SLPnWCt4NZTuT6Qs6QeUn-wOYPMaLh11DyZBNOuqiNWKjs_xPoi6C8jS1Jua0loTJXblDuMTDRL6-k82SByi7q8Yywr2TAdrotYbXF8kmaMmzW2gdQhkJs3xeNm3RyoIZOiU-7uzykSG8EkC0bx3mhIGVW_IOpzD2Xo2abbweR-PyX5z07qn9F1BHScdXViDmMNq2FU25D9K4FrRUqg8k5jpzkFrhcyPuw_hwB_BheNZgxulBbKy686qC6vTT41kZceD34PdBlMpzPctsK60GQWow8qs_OTQjD5ff_sTrNk4wzFpzo74ctcHOCZavW3gnZjhrMO9yHKGUBvgQJCiQ3C9nAkEP4pSOtk3nYgNLaWFftUYS_JKf36PcpM-YJYZEO33ayrcK19fp0aZbP11W1RpCs3jOaVWGwsS3xFE-4w_0xTbWoJACBgRENy_k\",\n  \"dp\": \"qtwBo39K7eDKXoyXn1YUwk8hzaNwhDqfhWPMGHiPjS9W5PLdEpfaxkoMK38oiYkb0ohmEe_z54fmMTAD037lYAbQpW-Al8z1J0qfFEmSgmCVHL80u8Tvq6OtCJojJUDDMEBL-s67FGepXekjNCyS7S1zXJ_CFx619VQv5hadLga2p5TL8pYBjNBfS9FKFeZmaIF6tkz_fNEwud9kOXW1gOcpbgTmBZgDxlHbCiQcL44q7vdKjwHY1a6bi9cf9uvuJ7E_3ysWycTKaH3q0lveTe8I1ovZy2QbuvzEKuF9P_9B8rZiWYbPx6H9bPzd1TisEH9the3R86ILLkqZwGZhZQ\",\n  \"dq\": \"zJ8N9S677F1s5YNYJ0LyzvL9bVcjaAwA-xUjDIxRhOMJWL_I-spBKfsOSwuFtr_KRSFj1ui25gIo8KJxsC1-1PBvjsM0OzjlvmaTqzsK1SFA6yt9Wh5VQ8BvqfH25g3JqHcAOqqYsymMFyq9c2ycaq9uYG-sxXiOYP3XCoZn_KsTnMZi0LLAL6A6BQoHSDDhnUHdPMrcZ8ePFjXowFKFlBWCOj0wWugpHc21TQFIeN9mfWAuyfEqyQP0G3FS60e3JW2B2NNZiui_o6lmSLnacLz51htpe23lgsUcJHkernow7-nOsWhvBdR90j69YUzowitL6WyJ_DEc1AehGYpOUw\",\n  \"e\": \"AQAB\",\n  \"kid\": \"key1\",\n  \"kty\": \"RSA\",\n  \"n\": \"2zNqGZKiogCjODBpzyRvwFlZ6hYaxJ4ZwYeFoN24eq_yHJB5OtEtgbUZ71lPkSqawLa-5qTtm1nBWY3ZAFVh9XC4fJsHWSIrwR7Mk9PLKWyAGFLyyGJy8srwdoxSUDbWa2CMeRsUaP_Syr_iytx1Kn9S9RRMrdC7PkMWaKx1KWQmIplrJx6qAiFlsTRvDFT0Ysfm0Vkti6xqTVYSc_bnObjLfiQ6UCKqF9fQDUJNFGLAeBAhkIRcBxp5G7PEiB5QOoRTrb4aqBIdxdjMWqjjfHTmm3EPqtIWsOjWRV1FsGyPkvolcBZXaNX-jf0oY1_7AryFujAFDslzGxg071yXRF9T_Brd1DW8paULQ2Vwkhc1d2c6Ioi-0D6255jlBKAVl-h3yedKWzYe5eyCijHZRs2jV1a3NX7ixzorcXH8GHB7PgM5lyZB5Rpf9-49MgW9Vo_b7nBCvEsN8uTc8jRyeG1zPTddAQ-tsMEmhsSa55EbQT6wk_nOu7xV-7eUAW8jwijJiDPOgDPmsOHtjoYx6BgcxCOYZ71s5g6qaKiCMecFpl7S1fxoIXcgjBNvv2Gzs6plRW74R6cVcohOfGVA7e0ULv1KOqJw6H-TjRmHBXQnw_K1biwYsL0SnE1Gu-iZC1_ktVuI8vf9k6m53HC_3_xrx0zqsad0fvIjpjRj2-E\",\n  \"p\": \"_9QXKH2TREzUqChGiRrSrKeURTuufWRr8dePBurE5xbqd3Edc360J3jifwfxW9jGRUwehVEQFMAPToPQP3aVLwlroVg5CHFmt6BOChZJ1ZpYfNxvwIQDyxmcGtpGKHkMZMJj_C3XhYULz94ham8w9t3Ps5A2CTLs8erDtm_22zXw8nB7AeUMu0_QEJEtXrG12tMcsVUiG94QFx1udu2d_XortXQlEoFz4KMGQhYBQultOe1o7awgwBHhh9XdSzPifyYArk9qBQEKx-mPZsFFJ46e3IaF-pVfP15J5x4NOhTDRC_NX2ZlXIyiNw_X3cmpMBvgEuA9lY15dQtD0_iqGw\",\n  \"q\": \"21kJj4Xvm2jX1-c8HIl4TAhPKI5470cPEx-8eViGO9KEsfc45T54a3shE3dP-YY6jQkpZritNzBnuSGaxSCJFhF63XZGYdh3p2GG73voO8dLqZNTFlitKaRA4UA_4byoimKdPaDR01Bhe9XCzIJCJfYqDGlTD2tIWcsytKwK0O9QkUqg-1ROlK02CMS4tBa8fzEXCYSnsB9iJUNOiLrHb6JdUUcOnCWvmnYFHIwhbH891Dhg9CMcCOwNWL1LGiCYilW-reM1pRHHB3H5b0_gwbg3DQ6dv4VmOCmzfNM0aTSvwkYQkfMQIF_SM8QWF6r9RSunMsoz_AKIjZ3yNj1xsw\",\n  \"qi\": \"gQh-bEfYunCcUKXuaBNuyesAAI8F6tWgwMtXqr6X_Np_GvtDdjho2YP14Jtx2_kxDDZPSnP_h003kM6OdJdF469-s-AuRXeqX99yHMfWDYEkXxkp4WsmsKQgg5mQNsBr4d4zHyzsqc1ZKf2mL9zxb5dnVgQjVKrYGgsnBlfZeP-Cz_6c1CZ1YkoxiH52dNdQPfPJUTSUlIgRs2BgCbszQHOO6a1qwkQOjhhUX3-_KF6G4agT2NmZrb_O67GHzIoqXpWZykn93cJm5119BF9dAQbQx4vl0daMuPrh8UwMYx7GO3iNL5tl_wBc77Z4bZu8fn-XzHL4bb3mSjg5DqntKQ\"\n}`\n\ntype JwkManager struct{}\n\nfunc (m JwkManager) GenerateKey() (jwk.Key, error) {\n\treturn nil, errors.New(\"not implemented\")\n}\n\nfunc (m JwkManager) GetPublicKeys() (jwk.Set, error) {\n\tkey, err := getJwk()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpublicKey, err := jwk.PublicKeyOf(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tset := jwk.NewSet()\n\terr = set.AddKey(publicKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn set, nil\n}\n\nfunc (m JwkManager) GetSigningKey() (jwk.Key, error) {\n\treturn getJwk()\n}\n\n// Sign a JWT with the signing key and returns it\nfunc (m JwkManager) Sign(token jwt.Token) ([]byte, error) {\n\tkey, err := m.GetSigningKey()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get signing key: %w\", err)\n\t}\n\tsigned, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, key))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to sign jwt: %w\", err)\n\t}\n\treturn signed, nil\n}\n\n// Verify verifies a JWT, using the verificationKeys and returns the parsed JWT\nfunc (m JwkManager) Verify(signed []byte) (jwt.Token, error) {\n\tkeys, err := m.GetPublicKeys()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get public keys: %w\", err)\n\t}\n\ttoken, err := jwt.Parse(signed, jwt.WithKeySet(keys))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to verify jwt: %w\", err)\n\t}\n\treturn token, nil\n}\n\nfunc getJwk() (jwk.Key, error) {\n\treturn jwk.ParseKey([]byte(privateKey))\n}\n"
  },
  {
    "path": "backend/test/mailslurper.go",
    "content": "package test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/google/uuid\"\n\t\"github.com/ory/dockertest/v3\"\n\t\"github.com/ory/dockertest/v3/docker\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/smtp\"\n\t\"time\"\n)\n\ntype TestMailslurper struct {\n\tId       string\n\tpool     *dockertest.Pool\n\tresource *dockertest.Resource\n\thttpUrl  string\n\tSmtpHost string\n\tSmtpPort string\n}\n\nfunc StartMailslurper() (*TestMailslurper, error) {\n\tpool, err := dockertest.NewPool(\"\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not construct pool: %w\", err)\n\t}\n\n\terr = pool.Client.Ping()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not connect to docker: %w\", err)\n\t}\n\n\tid := uuid.New().String()\n\toptions := getMailslurperOptions(id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not create docker run options: %w\", err)\n\t}\n\n\tresource, err := pool.RunWithOptions(options, func(config *docker.HostConfig) {\n\t\tconfig.AutoRemove = true\n\t\tconfig.RestartPolicy = docker.RestartPolicy{Name: \"no\"}\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not start resource: %w\", err)\n\t}\n\n\thostAndPort := resource.GetHostPort(\"8085/tcp\")\n\tdsn := fmt.Sprintf(\"http://%s\", hostAndPort)\n\n\t_ = resource.Expire(120)\n\n\tpool.MaxWait = 30 * time.Second\n\tif err = pool.Retry(func() error {\n\t\t_, err = http.Get(fmt.Sprintf(\"%s/mail\", dsn))\n\t\treturn err\n\t}); err != nil {\n\t\treturn nil, fmt.Errorf(\"could not connect to docker: %w\", err)\n\t}\n\n\tsmtpPort := resource.GetPort(\"2500/tcp\")\n\tsmtpHostAndPort := resource.GetHostPort(\"2500/tcp\")\n\tif err = pool.Retry(func() error {\n\t\tclient, err := smtp.Dial(fmt.Sprintf(\"%s\", smtpHostAndPort))\n\t\tdefer client.Close()\n\t\treturn err\n\t}); err != nil {\n\t\treturn nil, fmt.Errorf(\"could not connect to SMTP port: %w\", err)\n\t}\n\n\treturn &TestMailslurper{\n\t\tId:       id,\n\t\tpool:     pool,\n\t\tresource: resource,\n\t\thttpUrl:  dsn,\n\t\tSmtpHost: \"localhost\",\n\t\tSmtpPort: smtpPort,\n\t}, nil\n}\n\nfunc PurgeMailslurper(instance *TestMailslurper) error {\n\tif instance == nil {\n\t\treturn nil\n\t}\n\tif err := instance.pool.Purge(instance.resource); err != nil {\n\t\treturn fmt.Errorf(\"could not purge resource: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc getMailslurperOptions(id string) *dockertest.RunOptions {\n\treturn &dockertest.RunOptions{\n\t\tRepository: \"marcopas/docker-mailslurper\",\n\t\tTag:        \"latest\",\n\t\tExposedPorts: []string{\n\t\t\t\"8085/tcp\",\n\t\t\t\"2500/tcp\",\n\t\t},\n\t\tName: fmt.Sprintf(\"mailslurper-%s\", id),\n\t}\n}\n\ntype GetEmailResponse struct {\n\tMailItems    []GetEmailResponseMailItem `json:\"mailItems\"`\n\tTotalRecords int                        `json:\"totalRecords\"`\n}\n\ntype GetEmailResponseMailItem struct {\n\tId          string   `json:\"id\"`\n\tDateSent    string   `json:\"dateSent\"`\n\tFromAddress string   `json:\"fromAddress\"`\n\tToAddresses []string `json:\"toAddresses\"`\n\tSubject     string   `json:\"subject\"`\n\tBody        string   `json:\"body\"`\n\tContentType string   `json:\"contentType\"`\n}\n\nfunc (m *TestMailslurper) GetEmails() (*GetEmailResponse, error) {\n\tm.addBuffer()\n\n\tresponse, err := http.Get(fmt.Sprintf(\"%s/mail\", m.httpUrl))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get emails from mailslurper: %w\", err)\n\t}\n\n\tdefer response.Body.Close()\n\n\tbody, err := io.ReadAll(response.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read response body: %w\", err)\n\t}\n\n\tvar result GetEmailResponse\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal response body: %w\", err)\n\t}\n\n\treturn &result, nil\n}\n\ntype DeleteEmailsRequest struct {\n\tPruneCode string `json:\"pruneCode\" required:\"true\"`\n}\n\nfunc (m *TestMailslurper) DeleteEmails() error {\n\tm.addBuffer()\n\n\trequest := DeleteEmailsRequest{\n\t\tPruneCode: \"all\",\n\t}\n\n\tbodyJson, err := json.Marshal(request)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal request body: %w\", err)\n\t}\n\n\thttpReq, err := http.NewRequest(http.MethodDelete, fmt.Sprintf(\"%s/mail\", m.httpUrl), bytes.NewReader(bodyJson))\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\n\tclient := http.Client{}\n\n\tresponse, err := client.Do(httpReq)\n\tdefer response.Body.Close()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete emails from mailslurper: %w\", err)\n\t}\n\n\tif response.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"failed to delete emails from mailslurper: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (m *TestMailslurper) addBuffer() {\n\t// Allow buffer time for HTTP server to catch up with received emails\n\ttime.Sleep(15 * time.Millisecond)\n}\n"
  },
  {
    "path": "backend/test/suite.go",
    "content": "package test\n\nimport (\n\t\"fmt\"\n\t\"github.com/go-testfixtures/testfixtures/v3\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gobuffalo/pop/v6/logging\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"testing\"\n)\n\ntype Suite struct {\n\tsuite.Suite\n\tStorage         persistence.Storage\n\tDB              *TestDB\n\tEmailServer     *TestMailslurper\n\tName            string // used for database docker container name, so that tests can run in parallel\n\tWithEmailServer bool\n}\n\nfunc (s *Suite) SetupSuite() {\n\tif testing.Short() {\n\t\treturn\n\t}\n\tpop.SetLogger(testLogger)\n\t//pop.Debug = true\n\tif s.Name == \"\" {\n\t\tvar err error\n\t\tid, err := uuid.NewV4()\n\t\tif err != nil {\n\t\t\ts.Fail(\"failed to generate database container name\")\n\t\t}\n\t\ts.Name = id.String()\n\t}\n\tdialect := \"postgres\"\n\tdb, err := StartDB(s.Name, dialect)\n\ts.Require().NoError(err)\n\tstorage, err := persistence.New(config.Database{\n\t\tUrl: db.DatabaseUrl,\n\t})\n\ts.Require().NoError(err)\n\n\tif s.WithEmailServer {\n\t\ts.EmailServer, err = StartMailslurper()\n\t\ts.Require().NoError(err)\n\t}\n\n\ts.Storage = storage\n\ts.DB = db\n}\n\nfunc (s *Suite) SetupTest() {\n\tif s.DB != nil {\n\t\terr := s.Storage.MigrateUp()\n\t\ts.NoError(err)\n\t}\n}\n\nfunc (s *Suite) TearDownTest() {\n\tif s.DB != nil {\n\t\terr := s.Storage.MigrateDown(-1)\n\t\ts.NoError(err)\n\t}\n\tif s.EmailServer != nil {\n\t\ts.NoError(s.EmailServer.DeleteEmails())\n\t}\n}\n\nfunc (s *Suite) TearDownSuite() {\n\tif s.DB != nil {\n\t\ts.NoError(PurgeDB(s.DB))\n\t}\n\tif s.EmailServer != nil {\n\t\ts.NoError(PurgeMailslurper(s.EmailServer))\n\t}\n}\n\n// LoadFixtures loads predefined data from the path in the database.\nfunc (s *Suite) LoadFixtures(path string) error {\n\tfixtures, err := testfixtures.New(\n\t\ttestfixtures.Database(s.DB.DbCon),\n\t\ttestfixtures.Dialect(s.DB.Dialect),\n\t\ttestfixtures.Directory(path),\n\t\ttestfixtures.SkipResetSequences(),\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create testfixtures: %w\", err)\n\t}\n\n\terr = fixtures.Load()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not load fixtures: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc testLogger(level logging.Level, s string, args ...interface{}) {\n\n}\n"
  },
  {
    "path": "backend/thirdparty/claims.go",
    "content": "package thirdparty\n\nimport (\n\t\"errors\"\n\n\tzeroLogger \"github.com/rs/zerolog/log\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/utils\"\n)\n\ntype ClaimsAddress struct {\n\tFormatted  string `json:\"formatted,omitempty\" mapstructure:\"formatted,omitempty\"`\n\tLocality   string `json:\"locality,omitempty\" mapstructure:\"locality,omitempty\"`\n\tPostalCode string `json:\"postal_code,omitempty\" mapstructure:\"postal_code,omitempty\"`\n\tRegion     string `json:\"region,omitempty\" mapstructure:\"region,omitempty\"`\n\tStreet     string `json:\"street_address,omitempty\" mapstructure:\"street_address,omitempty\"`\n}\n\ntype Claims struct {\n\t// Reserved claims\n\tIssuer  string  `json:\"iss,omitempty\" mapstructure:\"iss,omitempty\"`\n\tSubject string  `json:\"sub,omitempty\" mapstructure:\"sub,omitempty\"`\n\tAud     string  `json:\"aud,omitempty\" mapstructure:\"aud,omitempty\"`\n\tIat     float64 `json:\"iat,omitempty\" mapstructure:\"iat,omitempty\"`\n\tExp     float64 `json:\"exp,omitempty\" mapstructure:\"exp,omitempty\"`\n\n\t// Default profile claims\n\tAddress           *ClaimsAddress `json:\"address,omitempty\" mapstructure:\"address,omitempty\"`\n\tBirthdate         string         `json:\"birthdate,omitempty\" mapstructure:\"birthdate,omitempty\"`\n\tEmail             string         `json:\"email,omitempty\" mapstructure:\"email,omitempty\"`\n\tEmailVerified     bool           `json:\"email_verified,omitempty\" mapstructure:\"email_verified,omitempty\"`\n\tFamilyName        string         `json:\"family_name,omitempty\" mapstructure:\"family_name,omitempty\"`\n\tGender            string         `json:\"gender,omitempty\" mapstructure:\"gender,omitempty\"`\n\tGivenName         string         `json:\"given_name,omitempty\" mapstructure:\"given_name,omitempty\"`\n\tLocale            string         `json:\"locale,omitempty\" mapstructure:\"locale,omitempty\"`\n\tMiddleName        string         `json:\"middle_name,omitempty\" mapstructure:\"middle_name,omitempty\"`\n\tName              string         `json:\"name,omitempty\" mapstructure:\"name,omitempty\"`\n\tNickName          string         `json:\"nickname,omitempty\" mapstructure:\"nickname,omitempty\"`\n\tPhone             string         `json:\"phone,omitempty\" mapstructure:\"phone,omitempty\"`\n\tPhoneVerified     bool           `json:\"phone_verified,omitempty\" mapstructure:\"phone_verified,omitempty\"`\n\tPicture           string         `json:\"picture,omitempty\" mapstructure:\"picture,omitempty\"`\n\tPreferredUsername string         `json:\"preferred_username,omitempty\" mapstructure:\"preferred_username,omitempty\"`\n\tProfile           string         `json:\"profile,omitempty\" mapstructure:\"profile,omitempty\"`\n\tUpdatedAt         string         `json:\"updated_at,omitempty\" mapstructure:\"updated_at,omitempty\"`\n\tWebsite           string         `json:\"website,omitempty\" mapstructure:\"website,omitempty\"`\n\tZoneInfo          string         `json:\"zoneinfo,omitempty\" mapstructure:\"zoneinfo,omitempty\"`\n\n\t// Custom profile claims that are oidc specific\n\tCustomClaims map[string]interface{} `json:\"custom_claims,omitempty\" mapstructure:\"custom_claims,remain,omitempty\"`\n}\n\ntype claimWarning struct {\n\tField  string\n\tReason string\n\tValue  string\n}\n\nfunc (c *Claims) ProviderProfile() (models.ProviderProfile, []claimWarning) {\n\tif c == nil {\n\t\treturn models.ProviderProfile{}, nil\n\t}\n\n\tprofile := models.ProviderProfile{\n\t\tName:       c.Name,\n\t\tGivenName:  c.GivenName,\n\t\tFamilyName: c.FamilyName,\n\t\tPicture:    c.Picture,\n\t}\n\n\tvar warnings []claimWarning\n\n\tif profile.Picture != \"\" {\n\t\tif err := utils.ValidatePictureURL(profile.Picture); err != nil {\n\t\t\treason := \"invalid\"\n\t\t\tvar perr utils.PictureURLError\n\t\t\tif errors.As(err, &perr) {\n\t\t\t\treason = perr.Reason\n\t\t\t}\n\n\t\t\twarnings = append(warnings, claimWarning{\n\t\t\t\tField:  \"picture\",\n\t\t\t\tReason: reason,\n\t\t\t\tValue:  profile.Picture,\n\t\t\t})\n\t\t\tprofile.Picture = \"\"\n\t\t}\n\t}\n\n\treturn profile, warnings\n}\n\nfunc (c *Claims) ProviderProfileWithLogging(operation string, providerID string) models.ProviderProfile {\n\tprofile, warnings := c.ProviderProfile()\n\tlogInvalidClaimWarnings(operation, providerID, warnings)\n\treturn profile\n}\n\nfunc logInvalidClaimWarnings(operation string, providerID string, warnings []claimWarning) {\n\tif len(warnings) == 0 {\n\t\treturn\n\t}\n\n\tl := zeroLogger.With().\n\t\tStr(\"component\", \"thirdparty\").\n\t\tStr(\"operation\", operation).\n\t\tStr(\"provider_id\", providerID).\n\t\tLogger()\n\n\tfor _, w := range warnings {\n\t\tl.Warn().\n\t\t\tStr(\"claim\", w.Field).\n\t\t\tStr(\"validation_reason\", w.Reason).\n\t\t\tMsg(\"ignored invalid claim while syncing provider profile\")\n\t}\n}\n"
  },
  {
    "path": "backend/thirdparty/error.go",
    "content": "package thirdparty\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n)\n\ntype ThirdPartyError struct {\n\tCode        string\n\tDescription string\n\tCause       error\n}\n\nfunc (e *ThirdPartyError) Query() string {\n\tq := url.Values{}\n\tq.Add(\"error\", e.Code)\n\n\tif e.Code == ErrorCodeServerError {\n\t\tq.Add(\"error_description\", \"an internal error has occurred\")\n\t} else if e.Description != \"\" {\n\t\tq.Add(\"error_description\", e.Description)\n\t}\n\treturn q.Encode()\n}\n\nfunc (e *ThirdPartyError) WithDescription(description string) *ThirdPartyError {\n\te.Description = description\n\treturn e\n}\n\nfunc (e *ThirdPartyError) WithCause(cause error) *ThirdPartyError {\n\te.Cause = cause\n\treturn e\n}\n\nfunc (e *ThirdPartyError) Error() string {\n\terr := fmt.Sprintf(\"thirdparty: %s\", e.Code)\n\n\tif e.Description != \"\" {\n\t\terr = fmt.Sprintf(\"%s: %s\", err, e.Description)\n\t}\n\n\tif e.Cause != nil {\n\t\treturn fmt.Sprintf(\"%s: %s\", err, e.Cause)\n\t}\n\treturn err\n}\n\nfunc NewThirdPartyError(code string, description string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: code, Description: description}\n}\n\nfunc ErrorInvalidRequest(desc string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: ErrorCodeInvalidRequest, Description: desc}\n}\n\nfunc ErrorServer(desc string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: ErrorCodeServerError, Description: desc}\n}\n\nfunc ErrorUserConflict(desc string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: ErrorCodeUserConflict, Description: desc}\n}\n\nfunc ErrorMultipleAccounts(desc string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: ErrorCodeMultipleAccounts, Description: desc}\n}\n\nfunc ErrorUnverifiedProviderEmail(desc string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: ErrorCodeUnverifiedProviderEmail, Description: desc}\n}\n\nfunc ErrorMaxNumberOfAddresses(desc string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: ErrorCodeMaxNumberOfAddresses, Description: desc}\n}\n\nfunc ErrorSignUpDisabled(desc string) *ThirdPartyError {\n\treturn &ThirdPartyError{Code: ErrorCodeSignUpDisabled, Description: desc}\n}\n\nconst (\n\tErrorCodeInvalidRequest          = \"invalid_request\"\n\tErrorCodeServerError             = \"server_error\"\n\tErrorCodeUserConflict            = \"user_conflict\"\n\tErrorCodeMultipleAccounts        = \"multiple_accounts\"\n\tErrorCodeUnverifiedProviderEmail = \"unverified_email\"\n\tErrorCodeMaxNumberOfAddresses    = \"email_maxnum\"\n\tErrorCodeSignUpDisabled          = \"signup_disabled\"\n)\n"
  },
  {
    "path": "backend/thirdparty/helper.go",
    "content": "package thirdparty\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"net/url\"\n\t\"strings\"\n)\n\nfunc IsAllowedRedirect(config config.ThirdParty, redirectTo string) bool {\n\tif redirectTo == \"\" {\n\t\treturn false\n\t}\n\n\tredirectTo = strings.TrimSuffix(redirectTo, \"/\")\n\n\tfor _, pattern := range config.AllowedRedirectURLMap {\n\t\tif pattern.Match(redirectTo) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc GetErrorUrl(redirectTo string, err error) string {\n\tvar redirectUrl string\n\tswitch v := err.(type) {\n\tcase *ThirdPartyError:\n\t\tredirectUrl = fmt.Sprintf(\"%s?%s\", redirectTo, v.Query())\n\tdefault:\n\t\tu := url.Values{}\n\t\tu.Add(\"error\", ErrorCodeServerError)\n\t\tu.Add(\"error_description\", \"an internal error has occurred\")\n\t\tredirectUrl = fmt.Sprintf(\"%s?%s\", redirectTo, u.Encode())\n\t}\n\treturn redirectUrl\n}\n"
  },
  {
    "path": "backend/thirdparty/helper_test.go",
    "content": "package thirdparty\n\nimport (\n\t\"errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestIsValidRedirectTo(t *testing.T) {\n\ttests := []struct {\n\t\tname                string\n\t\trequestedRedirect   string\n\t\tallowedRedirectURLs []string\n\t\terrorRedirectURL    string\n\t}{\n\t\t{\n\t\t\tname:                \"Exact match\",\n\t\t\trequestedRedirect:   \"https://foo.example.com\",\n\t\t\tallowedRedirectURLs: []string{\"https://foo.example.com\"},\n\t\t},\n\t\t{\n\t\t\tname:                \"Subdomain match\",\n\t\t\trequestedRedirect:   \"https://foo.example.com\",\n\t\t\tallowedRedirectURLs: []string{\"https://*.example.com\"},\n\t\t},\n\t\t{\n\t\t\tname:                \"Path match\",\n\t\t\trequestedRedirect:   \"https://foo.example.com/page/anotherPage\",\n\t\t\tallowedRedirectURLs: []string{\"https://foo.example.com/page/anotherPage\"},\n\t\t},\n\t\t{\n\t\t\tname:                \"Trailing slash ignored\",\n\t\t\trequestedRedirect:   \"https://foo.example.com/\",\n\t\t\tallowedRedirectURLs: []string{\"https://*.example.com\"},\n\t\t},\n\t\t{\n\t\t\tname:              \"Error redirect url, trailing slash ignored\",\n\t\t\trequestedRedirect: \"https://foo.example.com/error/\",\n\t\t\terrorRedirectURL:  \"https://foo.example.com/error\",\n\t\t},\n\t}\n\n\tfor _, testData := range tests {\n\t\tt.Run(testData.name, func(t *testing.T) {\n\t\t\tcfg := config.ThirdParty{\n\t\t\t\tAllowedRedirectURLS: testData.allowedRedirectURLs,\n\t\t\t}\n\n\t\t\tif testData.errorRedirectURL != \"\" {\n\t\t\t\tcfg.ErrorRedirectURL = testData.errorRedirectURL\n\t\t\t}\n\n\t\t\terr := cfg.PostProcess()\n\t\t\trequire.NoError(t, err)\n\n\t\t\tgot := IsAllowedRedirect(cfg, testData.requestedRedirect)\n\t\t\tassert.True(t, got)\n\t\t})\n\t}\n}\n\nfunc TestGetErrorUrl(t *testing.T) {\n\ttests := []struct {\n\t\tname                     string\n\t\tredirectTo               string\n\t\terror                    error\n\t\texpectedError            string\n\t\texpectedErrorDescription string\n\t}{\n\t\t{\n\t\t\tname:                     \"return url with server error when error is a third party server or invalid request error\",\n\t\t\tredirectTo:               \"https://foo.example.com\",\n\t\t\terror:                    ErrorServer(\"could not decode payload\"),\n\t\t\texpectedError:            ErrorCodeServerError,\n\t\t\texpectedErrorDescription: \"an internal error has occurred\",\n\t\t},\n\t\t{\n\t\t\tname:                     \"return url with third party error code and description\",\n\t\t\tredirectTo:               \"https://foo.example.com\",\n\t\t\terror:                    ErrorUserConflict(\"user already exists\"),\n\t\t\texpectedError:            ErrorCodeUserConflict,\n\t\t\texpectedErrorDescription: \"user already exists\",\n\t\t},\n\t\t{\n\t\t\tname:                     \"return url with server error when error is not a third party error\",\n\t\t\tredirectTo:               \"https://foo.example.com\",\n\t\t\terror:                    errors.New(\"non-third party error\"),\n\t\t\texpectedError:            ErrorCodeServerError,\n\t\t\texpectedErrorDescription: \"an internal error has occurred\",\n\t\t},\n\t}\n\n\tfor _, testData := range tests {\n\t\tt.Run(testData.name, func(t *testing.T) {\n\t\t\tgot := GetErrorUrl(testData.redirectTo, testData.error)\n\n\t\t\tu, err := url.Parse(got)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, testData.redirectTo, u.Scheme+\"://\"+u.Host)\n\t\t\tassert.Equal(t, testData.expectedError, u.Query().Get(\"error\"))\n\t\t\terrorDescription := u.Query().Get(\"error_description\")\n\t\t\tisCorrectErrorDescription := strings.Contains(errorDescription, testData.expectedErrorDescription)\n\t\t\tassert.Truef(t, isCorrectErrorDescription, \"error description '%s' does not contain '%s'\", errorDescription, testData.expectedErrorDescription)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/thirdparty/linking.go",
    "content": "package thirdparty\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n)\n\ntype AccountLinkingResult struct {\n\tType         models.AuditLogType\n\tUser         *models.User\n\tWebhookEvent *events.Event\n\tUserCreated  bool\n}\n\nfunc LinkAccount(tx *pop.Connection, cfg *config.Config, p persistence.Persister, userData *UserData, providerID string, isSaml bool, samlDomain *string, isFlow bool, userID *uuid.UUID) (*AccountLinkingResult, error) {\n\tif !isFlow {\n\t\tif cfg.Email.RequireVerification && !userData.Metadata.EmailVerified {\n\t\t\treturn nil, ErrorUnverifiedProviderEmail(\"third party provider email must be verified\")\n\t\t}\n\t}\n\n\t// Validate userData\n\tif userData == nil {\n\t\treturn nil, ErrorInvalidRequest(\"user data must be set\")\n\t}\n\n\t// Ensure the email is lowercase to avoid case sensitivity issues\n\tuserData.Metadata.Email = strings.ToLower(userData.Metadata.Email)\n\n\tidentity, err := p.GetIdentityPersister().Get(userData.Metadata.Subject, providerID)\n\tif err != nil {\n\t\treturn nil, ErrorServer(\"could not get identity\").WithCause(err)\n\t}\n\n\tif identity == nil {\n\t\tvar user *models.User\n\t\tif userID != nil {\n\t\t\tuser, err = p.GetUserPersisterWithConnection(tx).Get(*userID)\n\t\t} else {\n\t\t\tuser, err = p.GetUserPersisterWithConnection(tx).GetByEmailAddress(userData.Metadata.Email)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, ErrorServer(\"could not get email\").WithCause(err)\n\t\t}\n\n\t\tif user == nil {\n\t\t\treturn signUp(tx, cfg, p, userData, providerID, isSaml, samlDomain)\n\t\t} else {\n\t\t\treturn link(tx, cfg, p, userData, providerID, user, isSaml, samlDomain, userID != nil)\n\t\t}\n\t} else {\n\t\treturn signIn(tx, cfg, p, userData, identity)\n\t}\n}\n\nfunc link(tx *pop.Connection, cfg *config.Config, p persistence.Persister, userData *UserData, providerID string, user *models.User, isSaml bool, samlDomain *string, comesFromProfile bool) (*AccountLinkingResult, error) {\n\tif !isSaml {\n\t\tif strings.HasPrefix(providerID, \"custom_\") {\n\t\t\tprovider, ok := cfg.ThirdParty.CustomProviders[strings.TrimPrefix(providerID, \"custom_\")]\n\t\t\tif !ok {\n\t\t\t\treturn nil, ErrorServer(fmt.Sprintf(\"unknown provider: %s\", providerID))\n\t\t\t}\n\t\t\tif !provider.AllowLinking {\n\t\t\t\treturn nil, ErrorUserConflict(\"third party account linking for existing user with same email disallowed\")\n\t\t\t}\n\t\t} else {\n\t\t\tprovider := cfg.ThirdParty.Providers.Get(providerID)\n\t\t\tif provider == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unknown provider: %s\", providerID)\n\t\t\t}\n\n\t\t\tif !provider.AllowLinking {\n\t\t\t\treturn nil, ErrorUserConflict(\"third party account linking for existing user with same email disallowed\")\n\t\t\t}\n\t\t}\n\t}\n\n\tvar emailID *uuid.UUID\n\temail := user.GetEmailByAddress(userData.Metadata.Email)\n\tif email != nil {\n\t\temailID = &email.ID\n\t}\n\tvar userID *uuid.UUID = nil\n\tif !comesFromProfile {\n\t\tuserID = &user.ID\n\t}\n\n\t// TODO: when email is nil, we should create a new email and associate it with the identity\n\n\tuserDataMap, err := userData.ToMap()\n\tif err != nil {\n\t\treturn nil, ErrorServer(\"could not link account\").WithCause(err)\n\t}\n\n\tidentity, err := models.NewIdentity(providerID, userDataMap, emailID, userID)\n\tif err != nil {\n\t\treturn nil, ErrorServer(\"could not create identity\").WithCause(err)\n\t}\n\n\terr = p.GetIdentityPersisterWithConnection(tx).Create(*identity)\n\tif err != nil {\n\t\treturn nil, ErrorServer(\"could not create identity\").WithCause(err)\n\t}\n\n\tprofile := userData.Metadata.ProviderProfileWithLogging(\"thirdparty_link\", providerID)\n\n\tif user.SyncFromProviderProfile(profile) {\n\t\tuser.UpdatedAt = time.Now().UTC()\n\t\tif uerr := p.GetUserPersisterWithConnection(tx).Update(*user); uerr != nil {\n\t\t\treturn nil, ErrorServer(\"could not update user\").WithCause(uerr)\n\t\t}\n\t}\n\n\tif isSaml && samlDomain != nil && *samlDomain != \"\" && email != nil {\n\t\tif existingSamlIdentity := email.GetSamlIdentityForDomain(*samlDomain); existingSamlIdentity != nil {\n\t\t\tidentityToDeleteID := existingSamlIdentity.IdentityID\n\t\t\texistingSamlIdentity.IdentityID = identity.ID\n\n\t\t\terr = p.GetSamlIdentityPersisterWithConnection(tx).Update(*existingSamlIdentity)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, ErrorServer(\"could not update saml identity\").WithCause(err)\n\t\t\t}\n\n\t\t\terr = p.GetIdentityPersisterWithConnection(tx).Delete(models.Identity{ID: identityToDeleteID})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, ErrorServer(\"could not delete identity\").WithCause(err)\n\t\t\t}\n\t\t} else {\n\t\t\tsamlIdentityID, _ := uuid.NewV4()\n\t\t\tnow := time.Now().UTC()\n\t\t\tsamlIdentity := &models.SamlIdentity{\n\t\t\t\tID:         samlIdentityID,\n\t\t\t\tIdentityID: identity.ID,\n\t\t\t\tDomain:     *samlDomain,\n\t\t\t\tCreatedAt:  now,\n\t\t\t\tUpdatedAt:  now,\n\t\t\t}\n\n\t\t\terr = p.GetSamlIdentityPersisterWithConnection(tx).Create(*samlIdentity)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, ErrorServer(\"could not create saml identity\").WithCause(err)\n\t\t\t}\n\t\t}\n\t}\n\n\tu, terr := p.GetUserPersisterWithConnection(tx).Get(user.ID)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not get user\").WithCause(terr)\n\t}\n\n\treturn &AccountLinkingResult{\n\t\tType:         models.AuditLogThirdPartyLinkingSucceeded,\n\t\tUser:         u,\n\t\tWebhookEvent: nil,\n\t\tUserCreated:  false,\n\t}, nil\n}\n\nfunc signIn(tx *pop.Connection, cfg *config.Config, p persistence.Persister, userData *UserData, identity *models.Identity) (*AccountLinkingResult, error) {\n\tvar linkingResult *AccountLinkingResult\n\tvar webhookEvent events.Event\n\n\tuserPersister := p.GetUserPersisterWithConnection(tx)\n\temailPersister := p.GetEmailPersisterWithConnection(tx)\n\tidentityPersister := p.GetIdentityPersisterWithConnection(tx)\n\n\tvar terr error\n\temail := identity.Email\n\tif userData.Metadata.Email != \"\" && ((email == nil) || (email.Address != userData.Metadata.Email)) {\n\t\t// The primary email address at the provider has changed, check if the new provider email already exists\n\t\temail, terr = emailPersister.FindByAddress(userData.Metadata.Email)\n\t\tif terr != nil {\n\t\t\treturn nil, ErrorServer(\"could not get email\").WithCause(terr)\n\t\t}\n\n\t\tif email != nil {\n\t\t\tif email.UserID == nil {\n\t\t\t\t// The email already exists but is unassigned, claim it and associate the identity with it\n\t\t\t\temail.UserID = identity.Email.UserID\n\t\t\t\temail.Verified = userData.Metadata.EmailVerified\n\n\t\t\t\tterr = emailPersister.Update(*email)\n\t\t\t\tif terr != nil {\n\t\t\t\t\treturn nil, ErrorServer(\"could not update email\").WithCause(terr)\n\t\t\t\t}\n\n\t\t\t\tidentity.EmailID = &email.ID\n\t\t\t\twebhookEvent = events.UserUpdate\n\t\t\t} else if email.UserID.String() != identity.Email.UserID.String() {\n\t\t\t\t// The email is assigned to a different user, and so the identity is linked to multiple users. There\n\t\t\t\t// is not much we can do here but return an error.\n\t\t\t\treturn nil, ErrorMultipleAccounts(fmt.Sprintf(\"cannot identify associated user: '%s' is used by multiple accounts\", email.Address))\n\t\t\t} else {\n\t\t\t\t// The email is assigned to the same user. This can happen if the user creates an email with an\n\t\t\t\t// address equal to the new primary provider email prior to changing the primary mail at the\n\t\t\t\t// provider and then doing a sign in with the provider. We need to update the associated email in\n\t\t\t\t// the identity.\n\t\t\t\tidentity.EmailID = &email.ID\n\t\t\t}\n\t\t} else {\n\t\t\t// The email does not exist. Create a new one and associate the identity with it.\n\t\t\temailCount, err := emailPersister.CountByUserId(*identity.UserID)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, ErrorServer(\"failed to count user emails\").WithCause(err)\n\t\t\t}\n\n\t\t\tif emailCount >= cfg.Email.Limit {\n\t\t\t\treturn nil, ErrorMaxNumberOfAddresses(\"max number of email addresses reached\")\n\t\t\t}\n\n\t\t\temail = models.NewEmail(identity.UserID, userData.Metadata.Email)\n\t\t\temail.Verified = userData.Metadata.EmailVerified\n\t\t\tterr = emailPersister.Create(*email)\n\t\t\tif terr != nil {\n\t\t\t\treturn nil, ErrorServer(\"could not create email\").WithCause(terr)\n\t\t\t}\n\n\t\t\tidentity.EmailID = &email.ID\n\t\t\twebhookEvent = events.UserEmailCreate\n\t\t}\n\t}\n\n\tuserDataMap, err := userData.ToMap()\n\tif err != nil {\n\t\treturn nil, ErrorServer(\"could not link account\").WithCause(err)\n\t}\n\n\tidentity.Data = userDataMap\n\tterr = identityPersister.Update(*identity)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not update identity\").WithCause(terr)\n\t}\n\n\tuser, terr := userPersister.Get(*identity.UserID)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not get user\").WithCause(terr)\n\t}\n\n\tprofile := userData.Metadata.ProviderProfileWithLogging(\"thirdparty_sign_in\", identity.ProviderID)\n\n\tif user.SyncFromProviderProfile(profile) {\n\t\tuser.UpdatedAt = time.Now().UTC()\n\t\tif uerr := userPersister.Update(*user); uerr != nil {\n\t\t\treturn nil, ErrorServer(\"could not update user\").WithCause(uerr)\n\t\t}\n\t}\n\n\tlinkingResult = &AccountLinkingResult{\n\t\tType:         models.AuditLogThirdPartySignInSucceeded,\n\t\tUser:         user,\n\t\tWebhookEvent: &webhookEvent,\n\t\tUserCreated:  false,\n\t}\n\n\treturn linkingResult, nil\n}\n\nfunc signUp(tx *pop.Connection, cfg *config.Config, p persistence.Persister, userData *UserData, providerID string, isSaml bool, samlDomain *string) (*AccountLinkingResult, error) {\n\tif !cfg.Account.AllowSignup {\n\t\treturn nil, ErrorSignUpDisabled(\"account signup is disabled\")\n\t}\n\n\tvar linkingResult *AccountLinkingResult\n\n\tuserPersister := p.GetUserPersisterWithConnection(tx)\n\temailPersister := p.GetEmailPersisterWithConnection(tx)\n\tprimaryEmailPersister := p.GetPrimaryEmailPersisterWithConnection(tx)\n\tidentityPersister := p.GetIdentityPersisterWithConnection(tx)\n\n\temail, terr := emailPersister.FindByAddress(userData.Metadata.Email)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not get email\").WithCause(terr)\n\t}\n\n\tuser := models.NewUser()\n\n\tprofile := userData.Metadata.ProviderProfileWithLogging(\"thirdparty_sign_up\", providerID)\n\n\tuser.SyncFromProviderProfile(profile)\n\n\tterr = userPersister.Create(user)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not create user\").WithCause(terr)\n\t}\n\n\tif email != nil && email.UserID == nil {\n\t\t// There exists an email with the same address as the primary provider address, but it is not assigned\n\t\t// to any user yet, hence we assign the new user ID to this email.\n\t\temail.UserID = &user.ID\n\t\temail.Verified = userData.Metadata.EmailVerified\n\t\tterr = emailPersister.Update(*email)\n\t\tif terr != nil {\n\t\t\treturn nil, ErrorServer(\"could not update email\").WithCause(terr)\n\t\t}\n\n\t} else if userData.Metadata.Email != \"\" {\n\t\t// No email exists, create a new one using the provider user data email\n\t\temail = models.NewEmail(&user.ID, userData.Metadata.Email)\n\t\temail.Verified = userData.Metadata.EmailVerified\n\t\tterr = emailPersister.Create(*email)\n\t\tif terr != nil {\n\t\t\treturn nil, ErrorServer(\"failed to store email\").WithCause(terr)\n\t\t}\n\t}\n\n\tvar emailID *uuid.UUID = nil\n\tif email != nil {\n\t\temailID = &email.ID\n\t\tprimaryEmail := models.NewPrimaryEmail(email.ID, *email.UserID)\n\t\tterr = primaryEmailPersister.Create(*primaryEmail)\n\t\tif terr != nil {\n\t\t\treturn nil, ErrorServer(\"failed to store primary email\").WithCause(terr)\n\t\t}\n\t}\n\n\tuserDataMap, err := userData.ToMap()\n\tif err != nil {\n\t\treturn nil, ErrorServer(\"could not link account\").WithCause(err)\n\t}\n\n\tidentity, terr := models.NewIdentity(providerID, userDataMap, emailID, &user.ID)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not create identity\").WithCause(terr)\n\t}\n\n\tterr = identityPersister.Create(*identity)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not store identity\").WithCause(terr)\n\t}\n\n\tif isSaml && samlDomain != nil && *samlDomain != \"\" {\n\t\tsamlIdentityID, _ := uuid.NewV4()\n\t\tnow := time.Now().UTC()\n\t\tsamlIdentity := &models.SamlIdentity{\n\t\t\tID:         samlIdentityID,\n\t\t\tIdentityID: identity.ID,\n\t\t\tDomain:     *samlDomain,\n\t\t\tCreatedAt:  now,\n\t\t\tUpdatedAt:  now,\n\t\t}\n\n\t\terr = p.GetSamlIdentityPersisterWithConnection(tx).Create(*samlIdentity)\n\t\tif err != nil {\n\t\t\treturn nil, ErrorServer(\"could not store saml identity\").WithCause(err)\n\t\t}\n\t}\n\n\tu, terr := userPersister.Get(user.ID)\n\tif terr != nil {\n\t\treturn nil, ErrorServer(\"could not get user\").WithCause(terr)\n\t}\n\n\tevt := events.UserCreate\n\tlinkingResult = &AccountLinkingResult{\n\t\tType:         models.AuditLogThirdPartySignUpSucceeded,\n\t\tUser:         u,\n\t\tWebhookEvent: &evt,\n\t\tUserCreated:  true,\n\t}\n\n\treturn linkingResult, nil\n}\n"
  },
  {
    "path": "backend/thirdparty/provider.go",
    "content": "package thirdparty\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/mitchellh/mapstructure\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\ntype UserData struct {\n\tEmails   Emails\n\tMetadata *Claims\n}\n\nfunc (u *UserData) ToMap() (map[string]interface{}, error) {\n\tdata := make(map[string]interface{})\n\tif u.Metadata != nil {\n\t\terr := mapstructure.Decode(u.Metadata, &data)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not convert user data to map: %w\", err)\n\t\t}\n\t}\n\treturn data, nil\n}\n\ntype Emails []Email\n\ntype Email struct {\n\tEmail    string\n\tVerified bool\n\tPrimary  bool\n}\n\ntype OAuthProvider interface {\n\tAuthCodeURL(string, ...oauth2.AuthCodeOption) string\n\tGetUserData(*oauth2.Token) (*UserData, error)\n\tGetOAuthToken(string, ...oauth2.AuthCodeOption) (*oauth2.Token, error)\n\tID() string\n}\n\nfunc GetProvider(config config.ThirdParty, id string) (OAuthProvider, error) {\n\tidLower := strings.ToLower(id)\n\n\tif strings.HasPrefix(idLower, \"custom_\") {\n\t\treturn getCustomThirdPartyProvider(config, idLower)\n\t} else {\n\t\treturn getThirdPartyProvider(config, idLower)\n\t}\n}\n\nfunc getCustomThirdPartyProvider(config config.ThirdParty, id string) (OAuthProvider, error) {\n\tif config.CustomProviders != nil {\n\t\tif providerConfig, ok := config.CustomProviders[strings.TrimPrefix(id, \"custom_\")]; ok {\n\t\t\toauthProvider, err := NewCustomThirdPartyProvider(&providerConfig, config.RedirectURL)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn oauthProvider, nil\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"unknown provider: %s\", id)\n}\n\nfunc getThirdPartyProvider(config config.ThirdParty, id string) (OAuthProvider, error) {\n\tswitch id {\n\tcase \"google\":\n\t\treturn NewGoogleProvider(config.Providers.Google, config.RedirectURL)\n\tcase \"github\":\n\t\treturn NewGithubProvider(config.Providers.GitHub, config.RedirectURL)\n\tcase \"apple\":\n\t\treturn NewAppleProvider(config.Providers.Apple, config.RedirectURL)\n\tcase \"discord\":\n\t\treturn NewDiscordProvider(config.Providers.Discord, config.RedirectURL)\n\tcase \"microsoft\":\n\t\treturn NewMicrosoftProvider(config.Providers.Microsoft, config.RedirectURL)\n\tcase \"linkedin\":\n\t\treturn NewLinkedInProvider(config.Providers.LinkedIn, config.RedirectURL)\n\tcase \"facebook\":\n\t\treturn NewFacebookProvider(config.Providers.Facebook, config.RedirectURL)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown provider: %s\", id)\n\t}\n}\n\nfunc makeRequest(token *oauth2.Token, config *oauth2.Config, url string, dst interface{}) error {\n\tclient := config.Client(context.Background(), token)\n\tclient.Timeout = time.Second * 10\n\tres, err := client.Get(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer res.Body.Close()\n\n\tbodyBytes, _ := io.ReadAll(res.Body)\n\tdefer res.Body.Close()\n\tres.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))\n\n\tif res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusMultipleChoices {\n\t\treturn errors.New(string(bodyBytes))\n\t}\n\n\tif err := json.NewDecoder(res.Body).Decode(dst); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_apple.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\tzeroLogger \"github.com/rs/zerolog/log\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst (\n\tAppleAPIBase       = \"https://appleid.apple.com\"\n\tAppleAuthEndpoint  = AppleAPIBase + \"/auth/authorize\"\n\tAppleTokenEndpoint = AppleAPIBase + \"/auth/token\"\n\tAppleKeysEndpoint  = AppleAPIBase + \"/auth/keys\"\n)\n\nvar DefaultAppleScopes = []string{\n\t\"name\",\n\t\"email\",\n}\n\ntype appleProvider struct {\n\tconfig      config.ThirdPartyProvider\n\toauthConfig *oauth2.Config\n}\n\nfunc NewAppleProvider(config config.ThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, errors.New(\"apple provider requested but disabled\")\n\t}\n\n\treturn &appleProvider{\n\t\tconfig: config,\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:  AppleAuthEndpoint,\n\t\t\t\tTokenURL: AppleTokenEndpoint,\n\t\t\t},\n\t\t\tRedirectURL: redirectURL,\n\t\t\tScopes:      DefaultAppleScopes,\n\t\t},\n\t}, nil\n}\n\nfunc (a appleProvider) AuthCodeURL(state string, args ...oauth2.AuthCodeOption) string {\n\topts := append(args, oauth2.SetAuthURLParam(\"response_mode\", \"form_post\"))\n\n\tif prompt := a.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\n\tauthURL := a.oauthConfig.AuthCodeURL(state, opts...)\n\tu, _ := url.Parse(authURL)\n\tu.RawQuery = strings.ReplaceAll(u.RawQuery, \"+\", \"%20\")\n\tauthURL = u.String()\n\treturn authURL\n}\n\nfunc (a appleProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn a.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (a appleProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\trawIDToken, ok := token.Extra(\"id_token\").(string)\n\tif !ok {\n\t\treturn nil, errors.New(\"id_token missing\")\n\t}\n\n\tset, err := jwk.Fetch(context.Background(), AppleKeysEndpoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tparsedIDToken, err := jwt.Parse(\n\t\t[]byte(rawIDToken),\n\t\tjwt.WithKeySet(set),\n\t\tjwt.WithIssuer(AppleAPIBase),\n\t\tjwt.WithAudience(a.oauthConfig.ClientID),\n\t)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\temail, ok := parsedIDToken.PrivateClaims()[\"email\"].(string)\n\tif !ok {\n\t\treturn nil, errors.New(\"email claim expected to be of type string\")\n\t}\n\n\tvar emailVerified = false\n\temailVerifiedRaw, found := parsedIDToken.PrivateClaims()[\"email_verified\"]\n\tif found {\n\t\tswitch v := emailVerifiedRaw.(type) {\n\t\tcase string:\n\t\t\temailVerified, err = strconv.ParseBool(v)\n\t\t\tif err != nil {\n\t\t\t\tzeroLogger.Warn().Err(err).Msgf(\"could not parse 'email_verified' claim as bool\")\n\t\t\t}\n\t\tcase bool:\n\t\t\temailVerified = v\n\t\tdefault:\n\t\t\tzeroLogger.Warn().Msgf(\"'email_verified' claim is neither of type 'string' or 'bool'\")\n\t\t}\n\t}\n\n\tuserData := &UserData{\n\t\tEmails: []Email{{\n\t\t\tEmail:    email,\n\t\t\tVerified: emailVerified,\n\t\t\tPrimary:  true,\n\t\t}},\n\t\tMetadata: &Claims{\n\t\t\tIssuer:        parsedIDToken.Issuer(),\n\t\t\tSubject:       parsedIDToken.Subject(),\n\t\t\tEmail:         email,\n\t\t\tEmailVerified: emailVerified,\n\t\t},\n\t}\n\n\treturn userData, nil\n}\n\nfunc (a appleProvider) ID() string {\n\treturn a.config.ID\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_custom.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/coreos/go-oidc/v3/oidc\"\n\t\"github.com/mitchellh/mapstructure\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\ntype customProvider struct {\n\tconfig       *config.CustomThirdPartyProvider\n\toauthConfig  *oauth2.Config\n\toidcProvider *oidc.Provider\n}\n\nfunc NewCustomThirdPartyProvider(config *config.CustomThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, fmt.Errorf(\"provider %s is disabled\", config.ID)\n\t}\n\n\tcustomProvider := &customProvider{\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tScopes:       config.Scopes,\n\t\t\tRedirectURL:  redirectURL,\n\t\t},\n\t}\n\n\tif config.UseDiscovery {\n\t\tprovider, err := oidc.NewProvider(context.Background(), config.Issuer)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tcustomProvider.oidcProvider = provider\n\t\tcustomProvider.oauthConfig.Endpoint = customProvider.oidcProvider.Endpoint()\n\t} else {\n\t\tproviderConfig := oidc.ProviderConfig{\n\t\t\tIssuerURL:   config.Issuer,\n\t\t\tAuthURL:     config.AuthorizationEndpoint,\n\t\t\tTokenURL:    config.TokenEndpoint,\n\t\t\tUserInfoURL: config.UserinfoEndpoint,\n\t\t\t// Algorithms:  []string{\"RS256\"}, // TODO: What should be the value for this?\n\t\t}\n\n\t\tcustomProvider.oidcProvider = providerConfig.NewProvider(context.Background())\n\t\tcustomProvider.oauthConfig.Endpoint = customProvider.oidcProvider.Endpoint()\n\t}\n\n\tcustomProvider.config = config\n\treturn customProvider, nil\n}\n\nfunc (p customProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {\n\n\tif prompt := p.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\tif acrValues := p.config.AcrValues; len(acrValues) > 0 {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"acr_values\", strings.Join(acrValues, \" \")))\n\t}\n\n\treturn p.oauthConfig.AuthCodeURL(state, opts...)\n}\n\nfunc (p customProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn p.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (p customProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\ttokenSource := p.oauthConfig.TokenSource(context.Background(), token)\n\n\tuserInfo, err := p.oidcProvider.UserInfo(context.Background(), tokenSource)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// oidc.UserInfo does not make raw claims map publicly accessible,\n\t// hence the additional unmarshal via oidc.UserInfo.Claims method\n\tuserInfoClaims := make(map[string]interface{})\n\terr = userInfo.Claims(&userInfoClaims)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not get user data: %s\", err)\n\t}\n\n\tif p.config.AttributeMapping != nil {\n\t\tfor hankoClaim, providerClaim := range p.config.AttributeMapping {\n\t\t\tuserInfoClaims[hankoClaim] = userInfoClaims[providerClaim]\n\t\t\tdelete(userInfoClaims, providerClaim)\n\t\t}\n\t}\n\n\tvar claims Claims\n\terr = mapstructure.Decode(userInfoClaims, &claims)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not get user data: %s\", err)\n\t}\n\n\treturn &UserData{\n\t\tMetadata: &claims,\n\t}, nil\n}\n\nfunc (p customProvider) ID() string {\n\treturn p.config.ID\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_discord.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst (\n\tDiscordAPIBase            = \"https://discord.com/api\"\n\tDiscordOauthAuthEndpoint  = \"https://discord.com/oauth2/authorize\"\n\tDiscordOauthTokenEndpoint = DiscordAPIBase + \"/oauth2/token\"\n\tDiscordUserInfoEndpoint   = DiscordAPIBase + \"/users/@me\"\n)\n\nvar DefaultDiscordScopes = []string{\n\t\"identify\",\n\t\"email\",\n}\n\ntype discordProvider struct {\n\tconfig      config.ThirdPartyProvider\n\toauthConfig *oauth2.Config\n}\n\ntype DiscordUser struct {\n\tID         string `json:\"id\"`\n\tUsername   string `json:\"username\"`\n\tGlobalName string `json:\"global_name\"`\n\tAvatar     string `json:\"avatar\"`\n\tEmail      string `json:\"email\"`\n\tVerified   bool   `json:\"verified\"`\n}\n\n// NewDiscordProvider creates a Discord third party provider.\nfunc NewDiscordProvider(config config.ThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, errors.New(\"discord provider is disabled\")\n\t}\n\n\treturn &discordProvider{\n\t\tconfig: config,\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:  DiscordOauthAuthEndpoint,\n\t\t\t\tTokenURL: DiscordOauthTokenEndpoint,\n\t\t\t},\n\t\t\tScopes:      DefaultDiscordScopes,\n\t\t\tRedirectURL: redirectURL,\n\t\t},\n\t}, nil\n}\n\nfunc (p discordProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {\n\n\tif prompt := p.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\n\treturn p.oauthConfig.AuthCodeURL(state, opts...)\n}\n\nfunc (g discordProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn g.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (g discordProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\tvar user DiscordUser\n\tif err := makeRequest(token, g.oauthConfig, DiscordUserInfoEndpoint, &user); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata := &UserData{}\n\n\tif user.Email != \"\" {\n\t\tdata.Emails = append(data.Emails, Email{\n\t\t\tEmail:    user.Email,\n\t\t\tVerified: user.Verified,\n\t\t\tPrimary:  true,\n\t\t})\n\t}\n\n\tif len(data.Emails) <= 0 {\n\t\treturn nil, errors.New(\"unable to find email with Discord provider\")\n\t}\n\n\tdata.Metadata = &Claims{\n\t\tIssuer:        DiscordAPIBase,\n\t\tSubject:       user.ID,\n\t\tName:          user.GlobalName,\n\t\tPicture:       g.buildAvatarURL(user.ID, user.Avatar),\n\t\tEmail:         user.Email,\n\t\tEmailVerified: user.Verified,\n\t}\n\n\treturn data, nil\n}\n\nfunc (g discordProvider) buildAvatarURL(userID string, avatarHash string) string {\n\tif avatarHash == \"\" {\n\t\treturn \"\" // No image\n\t}\n\n\treturn fmt.Sprintf(\"https://cdn.discordapp.com/avatars/%s/%s.png\", userID, avatarHash)\n}\n\nfunc (g discordProvider) ID() string {\n\treturn g.config.ID\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_facebook.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"net/url\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst (\n\tFacebookAuthBase           = \"https://www.facebook.com\"\n\tFacebookAPIBase            = \"https://graph.facebook.com\"\n\tFacebookOauthAuthEndpoint  = FacebookAuthBase + \"/v21.0/dialog/oauth\"\n\tFacebookOauthTokenEndpoint = FacebookAPIBase + \"/v21.0/oauth/access_token\"\n\tFacebookUserInfoEndpoint   = FacebookAPIBase + \"/me\"\n)\n\nvar DefaultFacebookScopes = []string{\n\t\"email\", \"public_profile\",\n}\n\ntype facebookProvider struct {\n\tconfig      config.ThirdPartyProvider\n\toauthConfig *oauth2.Config\n}\n\ntype FacebookUser struct {\n\tID      string `json:\"id\"`\n\tName    string `json:\"name\"`\n\tEmail   string `json:\"email\"`\n\tPicture struct {\n\t\tData struct {\n\t\t\tURL string `json:\"url\"`\n\t\t} `json:\"data\"`\n\t} `json:\"picture\"`\n\tFirstName  string `json:\"first_name\"`\n\tMiddleName string `json:\"middle_name\"`\n\tLastName   string `json:\"last_name\"`\n}\n\n// NewFacebookProvider creates a Facebook third-party OAuth provider.\nfunc NewFacebookProvider(config config.ThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, errors.New(\"facebook provider is disabled\")\n\t}\n\n\treturn &facebookProvider{\n\t\tconfig: config,\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:  FacebookOauthAuthEndpoint,\n\t\t\t\tTokenURL: FacebookOauthTokenEndpoint,\n\t\t\t},\n\t\t\tScopes:      DefaultFacebookScopes,\n\t\t\tRedirectURL: redirectURL,\n\t\t},\n\t}, nil\n}\n\nfunc (f facebookProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {\n\n\tif prompt := f.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\n\treturn f.oauthConfig.AuthCodeURL(state, opts...)\n}\n\nfunc (f facebookProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn f.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (f facebookProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\tendpointURL, err := url.Parse(FacebookUserInfoEndpoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tendpointURLQuery := endpointURL.Query()\n\tendpointURLQuery.Add(\"fields\", \"id,name,email,picture,first_name,middle_name,last_name\")\n\n\t// Calculate appsecret_proof, see:\n\t// https://developers.facebook.com/docs/graph-api/guides/secure-requests/#appsecret_proof\n\thash := hmac.New(sha256.New, []byte(f.config.Secret))\n\thash.Write([]byte(token.AccessToken))\n\tappsecretProof := hex.EncodeToString(hash.Sum(nil))\n\n\tendpointURLQuery.Add(\"appsecret_proof\", appsecretProof)\n\tendpointURL.RawQuery = endpointURLQuery.Encode()\n\n\tvar fbUser FacebookUser\n\tif err = makeRequest(token, f.oauthConfig, endpointURL.String(), &fbUser); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif fbUser.Email == \"\" {\n\t\treturn nil, errors.New(\"unable to find email with Facebook provider\")\n\t}\n\n\tdata := &UserData{\n\t\tEmails: []Email{\n\t\t\t{\n\t\t\t\tEmail: fbUser.Email,\n\t\t\t\t// Consider the email as verified because a User node only returns an email if a valid\n\t\t\t\t// email address is available. See: https://developers.facebook.com/docs/graph-api/reference/user/\n\t\t\t\tVerified: true,\n\t\t\t\tPrimary:  true,\n\t\t\t},\n\t\t},\n\t\tMetadata: &Claims{\n\t\t\tIssuer:        FacebookAuthBase,\n\t\t\tSubject:       fbUser.ID,\n\t\t\tName:          fbUser.Name,\n\t\t\tPicture:       fbUser.Picture.Data.URL,\n\t\t\tEmail:         fbUser.Email,\n\t\t\tEmailVerified: true,\n\t\t\tGivenName:     fbUser.FirstName,\n\t\t\tMiddleName:    fbUser.MiddleName,\n\t\t\tFamilyName:    fbUser.LastName,\n\t\t},\n\t}\n\n\treturn data, nil\n}\n\nfunc (f facebookProvider) ID() string {\n\treturn f.config.ID\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_github.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst (\n\tGithubAuthBase           = \"https://github.com\"\n\tGithubAPIBase            = \"https://api.github.com\"\n\tGithubOauthAuthEndpoint  = GithubAuthBase + \"/login/oauth/authorize\"\n\tGithubOauthTokenEndpoint = GithubAuthBase + \"/login/oauth/access_token\"\n\tGithubUserInfoEndpoint   = GithubAPIBase + \"/user\"\n\tGitHubEmailsEndpoint     = GithubAPIBase + \"/user/emails\"\n)\n\nvar DefaultGitHubScopes = []string{\n\t\"user:email\", \"user:read\",\n}\n\ntype githubProvider struct {\n\tconfig      config.ThirdPartyProvider\n\toauthConfig *oauth2.Config\n}\n\ntype GithubUser struct {\n\tID        int    `json:\"id\"`\n\tUserName  string `json:\"login\"`\n\tEmail     string `json:\"email\"`\n\tName      string `json:\"name\"`\n\tAvatarURL string `json:\"avatar_url\"`\n}\n\ntype GithubUserEmail struct {\n\tEmail    string `json:\"email\"`\n\tPrimary  bool   `json:\"primary\"`\n\tVerified bool   `json:\"verified\"`\n}\n\nfunc NewGithubProvider(config config.ThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, errors.New(\"github provider requested but disabled\")\n\t}\n\n\treturn &githubProvider{\n\t\tconfig: config,\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\t// https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity\n\t\t\t\tAuthURL: GithubOauthAuthEndpoint,\n\t\t\t\t// https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github\n\t\t\t\tTokenURL: GithubOauthTokenEndpoint,\n\t\t\t},\n\t\t\tRedirectURL: redirectURL,\n\t\t\tScopes:      DefaultGitHubScopes,\n\t\t},\n\t}, nil\n}\n\nfunc (g githubProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {\n\n\tif prompt := g.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\n\treturn g.oauthConfig.AuthCodeURL(state, opts...)\n}\n\nfunc (g githubProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn g.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (g githubProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\tvar user GithubUser\n\n\t// https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user\n\tif err := makeRequest(token, g.oauthConfig, GithubUserInfoEndpoint, &user); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata := &UserData{\n\t\tMetadata: &Claims{\n\t\t\tIssuer:            GithubAuthBase,\n\t\t\tSubject:           strconv.Itoa(user.ID),\n\t\t\tName:              user.Name,\n\t\t\tPicture:           user.AvatarURL,\n\t\t\tPreferredUsername: user.UserName,\n\t\t},\n\t}\n\n\tvar emails []*GithubUserEmail\n\t// The user data 'email' value is the user's publicly visible email address. It is possible that the user\n\t// chose to not make this email public, hence the dedicated call to the 'emails' endpoint.\n\t// https://docs.github.com/en/rest/users/emails?apiVersion=2022-11-28#list-email-addresses-for-the-authenticated-user\n\tif err := makeRequest(token, g.oauthConfig, GitHubEmailsEndpoint, &emails); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, e := range emails {\n\t\tif e.Email != \"\" {\n\t\t\tdata.Emails = append(data.Emails, Email{Email: e.Email, Verified: e.Verified, Primary: e.Primary})\n\t\t}\n\n\t\tif e.Primary {\n\t\t\tdata.Metadata.Email = e.Email\n\t\t\tdata.Metadata.EmailVerified = e.Verified\n\t\t}\n\t}\n\n\tif len(data.Emails) <= 0 {\n\t\treturn nil, errors.New(\"unable to find email with GitHub provider\")\n\t}\n\n\treturn data, nil\n}\n\nfunc (g githubProvider) ID() string {\n\treturn g.config.ID\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_google.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst (\n\tGoogleAuthBase           = \"https://accounts.google.com\"\n\tGoogleAPIBase            = \"https://www.googleapis.com\"\n\tGoogleOauthAuthEndpoint  = GoogleAuthBase + \"/o/oauth2/auth\"\n\tGoogleOauthTokenEndpoint = GoogleAuthBase + \"/o/oauth2/token\"\n\tGoogleUserInfoEndpoint   = GoogleAPIBase + \"/oauth2/v3/userinfo\"\n)\n\nvar DefaultGoogleScopes = []string{\n\t\"email\",\n\t\"profile\",\n}\n\ntype googleProvider struct {\n\tconfig      config.ThirdPartyProvider\n\toauthConfig *oauth2.Config\n}\n\ntype GoogleUser struct {\n\tID            string `json:\"sub\"`\n\tName          string `json:\"name\"`\n\tPicture       string `json:\"picture\"`\n\tEmail         string `json:\"email\"`\n\tEmailVerified bool   `json:\"email_verified\"`\n\tGivenName     string `json:\"given_name\"`\n\tFamilyName    string `json:\"family_name\"`\n}\n\n// NewGoogleProvider creates a Google third party provider.\nfunc NewGoogleProvider(config config.ThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, errors.New(\"google provider is disabled\")\n\t}\n\n\treturn &googleProvider{\n\t\tconfig: config,\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:  GoogleOauthAuthEndpoint,\n\t\t\t\tTokenURL: GoogleOauthTokenEndpoint,\n\t\t\t},\n\t\t\tScopes:      DefaultGoogleScopes,\n\t\t\tRedirectURL: redirectURL,\n\t\t},\n\t}, nil\n}\n\nfunc (g googleProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {\n\n\tif prompt := g.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\n\treturn g.oauthConfig.AuthCodeURL(state, opts...)\n}\n\nfunc (g googleProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn g.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (g googleProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\tvar user GoogleUser\n\tif err := makeRequest(token, g.oauthConfig, GoogleUserInfoEndpoint, &user); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata := &UserData{}\n\n\tif user.Email != \"\" {\n\t\tdata.Emails = append(data.Emails, Email{\n\t\t\tEmail:    user.Email,\n\t\t\tVerified: user.EmailVerified,\n\t\t\tPrimary:  true,\n\t\t})\n\t}\n\n\tif len(data.Emails) <= 0 {\n\t\treturn nil, errors.New(\"unable to find email with Google provider\")\n\t}\n\n\tdata.Metadata = &Claims{\n\t\tIssuer:        GoogleAuthBase,\n\t\tSubject:       user.ID,\n\t\tName:          user.Name,\n\t\tPicture:       user.Picture,\n\t\tEmail:         user.Email,\n\t\tEmailVerified: user.EmailVerified,\n\t\tGivenName:     user.GivenName,\n\t\tFamilyName:    user.FamilyName,\n\t}\n\n\treturn data, nil\n}\n\nfunc (g googleProvider) ID() string {\n\treturn g.config.ID\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_linkedin.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/coreos/go-oidc/v3/oidc\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst (\n\tLinkedInIssuer = \"https://www.linkedin.com/oauth\"\n)\n\nvar DefaultLinkedinScopes = []string{\n\t\"openid\",\n\t\"profile\",\n\t\"email\",\n}\n\ntype LinkedinUser struct {\n\tID         string `json:\"sub\"`\n\tName       string `json:\"name\"`\n\tGivenName  string `json:\"given_name\"`\n\tFamilyName string `json:\"family_name\"`\n\tPicture    string `json:\"picture\"`\n\tLocale     struct {\n\t\tCountry  string `json:\"country\"`\n\t\tLanguage string `json:\"language\"`\n\t} `json:\"locale\"`\n\tEmail    string `json:\"email\"`\n\tVerified bool   `json:\"email_verified\"`\n}\n\ntype linkedInProvider struct {\n\tconfig       config.ThirdPartyProvider\n\toidcProvider *oidc.Provider\n\toauthConfig  *oauth2.Config\n}\n\n// NewLinkedInProvider creates a LinkedIn third party provider.\nfunc NewLinkedInProvider(config config.ThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, errors.New(\"linkedIn provider is disabled\")\n\t}\n\n\toidcProvider, err := oidc.NewProvider(context.Background(), LinkedInIssuer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tendpoint := oidcProvider.Endpoint()\n\n\treturn &linkedInProvider{\n\t\tconfig:       config,\n\t\toidcProvider: oidcProvider,\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tEndpoint:     endpoint,\n\t\t\tScopes:       DefaultLinkedinScopes,\n\t\t\tRedirectURL:  redirectURL,\n\t\t},\n\t}, nil\n}\n\nfunc (g linkedInProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {\n\n\tif prompt := g.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\n\treturn g.oauthConfig.AuthCodeURL(state, opts...)\n}\n\nfunc (g linkedInProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn g.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (g linkedInProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\tvar user LinkedinUser\n\tif err := makeRequest(token, g.oauthConfig, g.oidcProvider.UserInfoEndpoint(), &user); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata := &UserData{}\n\n\tif user.Email != \"\" {\n\t\tdata.Emails = append(data.Emails, Email{\n\t\t\tEmail:    user.Email,\n\t\t\tVerified: user.Verified,\n\t\t\tPrimary:  true,\n\t\t})\n\t}\n\n\tif len(data.Emails) <= 0 {\n\t\treturn nil, errors.New(\"unable to find email with LinkedIn provider\")\n\t}\n\n\tdata.Metadata = &Claims{\n\t\tIssuer:        LinkedInIssuer,\n\t\tSubject:       user.ID,\n\t\tName:          user.Name,\n\t\tFamilyName:    user.FamilyName,\n\t\tGivenName:     user.GivenName,\n\t\tPicture:       user.Picture,\n\t\tLocale:        fmt.Sprintf(\"%s-%s\", user.Locale.Country, user.Locale.Language),\n\t\tEmail:         user.Email,\n\t\tEmailVerified: user.Verified,\n\t}\n\n\treturn data, nil\n}\n\nfunc (g linkedInProvider) ID() string {\n\treturn g.config.ID\n}\n"
  },
  {
    "path": "backend/thirdparty/provider_microsoft.go",
    "content": "package thirdparty\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/mail\"\n\t\"regexp\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jws\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/mitchellh/mapstructure\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst (\n\tMicrosoftAuthBase           = \"https://login.microsoftonline.com/common\"\n\tMicrosoftKeysEndpoint       = \"https://login.microsoftonline.com/common/discovery/v2.0/keys\"\n\tMicrosoftOAuthAuthEndpoint  = MicrosoftAuthBase + \"/oauth2/v2.0/authorize\"\n\tMicrosoftOAuthTokenEndpoint = MicrosoftAuthBase + \"/oauth2/v2.0/token\"\n)\n\nvar DefaultScopes = []string{\n\t\"openid\",\n\t\"profile\",\n\t\"email\",\n}\n\ntype microsoftProvider struct {\n\tconfig      config.ThirdPartyProvider\n\toauthConfig *oauth2.Config\n}\n\ntype MicrosoftUser struct {\n\tID                string `json:\"id\"`\n\tName              string `json:\"displayName\"`\n\tEmail             string `json:\"mail\"`\n\tEmailVerified     bool   `json:\"email_verified\"`\n\tUserPrincipalName string `json:\"user_principal_name\"`\n}\n\n// NewMicrosoftProvider creates a Microsoft third party provider.\nfunc NewMicrosoftProvider(config config.ThirdPartyProvider, redirectURL string) (OAuthProvider, error) {\n\tif !config.Enabled {\n\t\treturn nil, errors.New(\"microsoft provider is disabled\")\n\t}\n\n\treturn &microsoftProvider{\n\t\tconfig: config,\n\t\toauthConfig: &oauth2.Config{\n\t\t\tClientID:     config.ClientID,\n\t\t\tClientSecret: config.Secret,\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:  MicrosoftOAuthAuthEndpoint,\n\t\t\t\tTokenURL: MicrosoftOAuthTokenEndpoint,\n\t\t\t},\n\t\t\tScopes:      DefaultScopes,\n\t\t\tRedirectURL: redirectURL,\n\t\t},\n\t}, nil\n}\n\nfunc (p microsoftProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {\n\n\tif prompt := p.config.Prompt; prompt != \"\" {\n\t\topts = append(opts, oauth2.SetAuthURLParam(\"prompt\", prompt))\n\t}\n\n\treturn p.oauthConfig.AuthCodeURL(state, opts...)\n}\n\nfunc (p microsoftProvider) GetOAuthToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\treturn p.oauthConfig.Exchange(context.Background(), code, opts...)\n}\n\nfunc (p microsoftProvider) GetUserData(token *oauth2.Token) (*UserData, error) {\n\trawIDToken, ok := token.Extra(\"id_token\").(string)\n\tif !ok {\n\t\treturn nil, errors.New(\"id_token missing\")\n\t}\n\n\tjwks, err := jwk.Fetch(context.Background(), MicrosoftKeysEndpoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tparsedIDToken, err := jwt.Parse(\n\t\t[]byte(rawIDToken),\n\t\t// JWKs of the JWKS (see 'MicrosoftKeysEndpoint') do not contain an 'alg' field. jws.WithKeySet expects this\n\t\t// field to be present per default, hence usage of the extra option jws.WithInferAlgorithmFromKey.\n\t\t// See the jwt.WithKeySet documentation.\n\t\tjwt.WithKeySet(jwks, jws.WithInferAlgorithmFromKey(true)),\n\t\tjwt.WithAudience(p.oauthConfig.ClientID),\n\t\tjwt.WithValidator(p.issuerValidator()),\n\t)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not parse id token: %w\", err)\n\t}\n\n\tif parsedIDToken == nil {\n\t\treturn nil, errors.New(\"could not parse id token\")\n\t}\n\n\tidTokenClaims, err := p.getIdTokenClaims(parsedIDToken.PrivateClaims())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not extract claims from id token: %w\", err)\n\t}\n\n\tif idTokenClaims == nil {\n\t\treturn nil, errors.New(\"id token claims must not be nil\")\n\t}\n\n\tvar email *Email\n\tif idTokenClaims.UserPrincipalName != \"\" {\n\t\t// Should be an email address, sanity check just to make sure.\n\t\tif address, err := mail.ParseAddress(idTokenClaims.UserPrincipalName); err == nil {\n\t\t\temail = &Email{\n\t\t\t\tEmail: address.Address,\n\t\t\t\t// Assume it is verified because it looks like UPN suffixes cannot be set to unverified domains.\n\t\t\t\tVerified: true,\n\t\t\t\tPrimary:  true,\n\t\t\t}\n\t\t}\n\t} else {\n\t\temailIsVerified, emailVerificationError := idTokenClaims.IsEmailVerified()\n\n\t\tif emailVerificationError != nil {\n\t\t\treturn nil, emailVerificationError\n\t\t}\n\n\t\tif emailIsVerified {\n\t\t\temail = &Email{\n\t\t\t\tEmail:    idTokenClaims.Email,\n\t\t\t\tVerified: true,\n\t\t\t\tPrimary:  true,\n\t\t\t}\n\t\t} else {\n\t\t\temail = &Email{\n\t\t\t\tEmail:    idTokenClaims.Email,\n\t\t\t\tVerified: false,\n\t\t\t\tPrimary:  true,\n\t\t\t}\n\t\t}\n\t}\n\n\tif email == nil {\n\t\treturn nil, errors.New(\"unable to find email with Microsoft provider\")\n\t}\n\n\tdata := &UserData{}\n\tdata.Emails = append(data.Emails, *email)\n\n\tdata.Metadata = &Claims{\n\t\tIssuer:            parsedIDToken.Issuer(),\n\t\tSubject:           parsedIDToken.Subject(),\n\t\tName:              idTokenClaims.Name,\n\t\tPreferredUsername: idTokenClaims.PreferredUsername,\n\t\tEmail:             email.Email,\n\t\tEmailVerified:     email.Verified,\n\t\tGivenName:         idTokenClaims.GivenName,\n\t\tFamilyName:        idTokenClaims.FamilyName,\n\t}\n\n\treturn data, nil\n}\n\nfunc (p microsoftProvider) ID() string {\n\treturn p.config.ID\n}\n\nfunc (p microsoftProvider) issuerValidator() jwt.ValidatorFunc {\n\tvar microsoftIssuerRegexp = regexp.MustCompile(\"^https://login[.]microsoftonline[.]com/([^/]+)/v2[.]0/?$\")\n\tvalidator := jwt.ValidatorFunc(func(_ context.Context, t jwt.Token) jwt.ValidationError {\n\t\tif !microsoftIssuerRegexp.MatchString(t.Issuer()) {\n\t\t\treturn jwt.NewValidationError(fmt.Errorf(`%s is not a valid microsoft issuer`, t.Issuer()))\n\t\t}\n\t\treturn nil\n\t})\n\treturn validator\n}\n\ntype microsoftIdTokenClaims struct {\n\tEmail                              string `mapstructure:\"email\"`\n\tName                               string `mapstructure:\"name\"`\n\tPreferredUsername                  string `mapstructure:\"preferred_username\"`\n\tUserPrincipalName                  string `mapstructure:\"upn\"`\n\tXMicrosoftEmailDomainOwnerVerified any    `mapstructure:\"xms_edov\"`\n\tFamilyName                         string `mapstructure:\"family_name\"`\n\tGivenName                          string `mapstructure:\"given_name\"`\n}\n\n// IsEmailVerified checks if the email used is verified. Functionality mainly derived from Supabase's GoTrue fork\n// See: https://github.com/supabase/gotrue/blob/master/internal/api/provider/oidc.go#L221\n// See also: https://www.descope.com/blog/post/noauth\nfunc (c *microsoftIdTokenClaims) IsEmailVerified() (bool, error) {\n\taddress, err := mail.ParseAddress(c.Email)\n\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"could not parse email from email claim: %w\", err)\n\t}\n\n\tif address == nil {\n\t\treturn false, errors.New(\"could not extract email from email claim\")\n\t}\n\n\temailVerified := false\n\n\tedov := c.XMicrosoftEmailDomainOwnerVerified\n\n\t// If xms_edov is not set, and an email is present or xms_edov is true,\n\t// only then is the email regarded as verified.\n\t// https://learn.microsoft.com/en-us/azure/active-directory/develop/migrate-off-email-claim-authorization#using-the-xms_edov-optional-claim-to-determine-email-verification-status-and-migrate-users\n\tif edov == nil {\n\t\t// An email is provided, but xms_edov is not -- probably not\n\t\t// configured, so we must assume the email is verified as Azure\n\t\t// will only send out a potentially unverified email address in\n\t\t// single-tenant apps (which we do not support - only the multi-tenant\n\t\t// + public account type).\n\t\temailVerified = true\n\t} else {\n\t\tedovBool := false\n\n\t\t// Azure can't be trusted with how they encode the xms_edov\n\t\t// claim. Sometimes it's \"xms_edov\": \"1\", sometimes \"xms_edov\": true.\n\t\tswitch v := edov.(type) {\n\t\tcase bool:\n\t\t\tedovBool = v\n\t\tcase string:\n\t\t\tedovBool = v == \"1\" || v == \"true\"\n\t\tdefault:\n\t\t\tedovBool = false\n\t\t}\n\n\t\temailVerified = edovBool\n\t}\n\n\treturn emailVerified, nil\n}\n\nfunc (p microsoftProvider) getIdTokenClaims(privateClaims map[string]interface{}) (*microsoftIdTokenClaims, error) {\n\tvar claims microsoftIdTokenClaims\n\terr := mapstructure.Decode(privateClaims, &claims)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode claims: %w\", err)\n\t}\n\n\treturn &claims, nil\n}\n"
  },
  {
    "path": "backend/thirdparty/state.go",
    "content": "package thirdparty\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/aes_gcm\"\n)\n\nfunc GenerateStateForFlowAPI(isFlow bool) func(*State) {\n\treturn func(state *State) {\n\t\tstate.IsFlow = isFlow\n\t}\n}\n\nfunc GenerateStateWithPKCECodeVerifier(codeVerifier string) func(state *State) {\n\treturn func(state *State) {\n\t\tif codeVerifier != \"\" {\n\t\t\tstate.CodeVerifier = codeVerifier\n\t\t}\n\t}\n}\n\n// GenerateStateWithUserID If the state is generated for a logged-in user, the OAuth request and response must only be used with the same already logged-in user.\nfunc GenerateStateWithUserID(userID uuid.UUID) func(*State) {\n\treturn func(state *State) {\n\t\tif userID != uuid.Nil {\n\t\t\tstate.UserID = &userID\n\t\t}\n\t}\n}\n\nfunc GenerateState(config *config.Config, provider string, redirectTo string, options ...func(*State)) ([]byte, error) {\n\tif provider == \"\" {\n\t\treturn nil, errors.New(\"provider must be present\")\n\t}\n\n\tif redirectTo == \"\" {\n\t\tredirectTo = config.ThirdParty.ErrorRedirectURL\n\t}\n\n\tnonce, err := crypto.GenerateRandomStringURLSafe(32)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not generate nonce: %w\", err)\n\t}\n\n\tnow := time.Now().UTC()\n\tstate := State{\n\t\tProvider:   provider,\n\t\tRedirectTo: redirectTo,\n\t\tIssuedAt:   now,\n\t\tExpiresAt:  now.Add(time.Minute * 5),\n\t\tNonce:      nonce,\n\t}\n\n\tfor _, option := range options {\n\t\toption(&state)\n\t}\n\n\tstateJson, err := json.Marshal(state)\n\n\taes, err := aes_gcm.NewAESGCM(config.Secrets.Keys)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not instantiate aesgcm: %w\", err)\n\t}\n\n\tencryptedState, err := aes.Encrypt(stateJson)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not encrypt state: %w\", err)\n\t}\n\n\treturn []byte(encryptedState), nil\n}\n\ntype State struct {\n\tProvider     string     `json:\"provider\"`\n\tRedirectTo   string     `json:\"redirect_to\"`\n\tIssuedAt     time.Time  `json:\"issued_at\"`\n\tExpiresAt    time.Time  `json:\"expires_at\"`\n\tNonce        string     `json:\"nonce\"`\n\tIsFlow       bool       `json:\"is_flow\"`\n\tCodeVerifier string     `json:\"code_verifier,omitempty\"`\n\tUserID       *uuid.UUID `json:\"user_id,omitempty\"`\n}\n\nfunc VerifyState(config *config.Config, state string, expectedState string) (*State, error) {\n\tdecodedState, err := decodeState(config, state)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not decode state: %w\", err)\n\t}\n\n\tif decodedState.CodeVerifier == \"\" {\n\t\tif expectedState == \"\" {\n\t\t\treturn nil, errors.New(\"expected state must not be empty\")\n\t\t}\n\t\tdecodedExpectedState, err := decodeState(config, expectedState)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not decode expectedState: %w\", err)\n\t\t}\n\n\t\tif decodedState.Nonce != decodedExpectedState.Nonce {\n\t\t\treturn nil, errors.New(\"could not verify state\")\n\t\t}\n\t}\n\n\tif time.Now().UTC().After(decodedState.ExpiresAt) {\n\t\treturn nil, errors.New(\"state is expired\")\n\t}\n\n\treturn decodedState, nil\n}\n\nfunc decodeState(config *config.Config, state string) (*State, error) {\n\taes, err := aes_gcm.NewAESGCM(config.Secrets.Keys)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not instantiate aesgcm: %w\", err)\n\t}\n\n\tdecryptedState, err := aes.Decrypt(state)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not decrypt state: %w\", err)\n\t}\n\n\tvar unmarshalledState State\n\terr = json.Unmarshal(decryptedState, &unmarshalledState)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not unmarshal state: %w\", err)\n\t}\n\treturn &unmarshalledState, nil\n}\n"
  },
  {
    "path": "backend/thirdparty/state_test.go",
    "content": "package thirdparty\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestThirdParty_GenerateState(t *testing.T) {\n\tcfg := &config.Config{\n\t\tSecrets: config.Secrets{Keys: []string{\"thirty-two-byte-long-test-secret\"}},\n\t\tThirdParty: config.ThirdParty{\n\t\t\tErrorRedirectURL: \"https://example.com/error\",\n\t\t},\n\t}\n\n\tstate, err := GenerateState(cfg, \"test-provider\", \"https://example.com\")\n\tassert.NoError(t, err)\n\tassert.NotNil(t, state)\n}\n\nfunc TestThirdParty_GenerateState_Error(t *testing.T) {\n\n\tredirectTo := \"https://example.com\"\n\n\ttests := []struct {\n\t\tname             string\n\t\tprovider         string\n\t\tredirectTo       string\n\t\tsecret           string\n\t\terrorRedirectUrl string\n\t\texpectedError    string\n\t}{\n\t\t{\n\t\t\tname:             \"provider is not present\",\n\t\t\tsecret:           \"test-secret\",\n\t\t\tprovider:         \"\",\n\t\t\tredirectTo:       redirectTo,\n\t\t\terrorRedirectUrl: redirectTo,\n\t\t\texpectedError:    \"provider must be present\",\n\t\t},\n\t\t{\n\t\t\tname:             \"provider is not present\",\n\t\t\tsecret:           \"\",\n\t\t\tprovider:         \"test-provider\",\n\t\t\tredirectTo:       redirectTo,\n\t\t\terrorRedirectUrl: redirectTo,\n\t\t\texpectedError:    \"could not instantiate aesgcm\",\n\t\t},\n\t}\n\tfor _, testData := range tests {\n\t\tt.Run(testData.name, func(t *testing.T) {\n\t\t\tcfg := &config.Config{\n\t\t\t\tSecrets:    config.Secrets{Keys: []string{testData.secret}},\n\t\t\t\tThirdParty: config.ThirdParty{ErrorRedirectURL: testData.errorRedirectUrl},\n\t\t\t}\n\t\t\t_, err := GenerateState(cfg, testData.provider, testData.redirectTo)\n\t\t\tassert.NotNil(t, err)\n\t\t\tassert.True(t, strings.Contains(err.Error(), testData.expectedError))\n\t\t})\n\t}\n}\n\nfunc TestThirdParty_VerifyState(t *testing.T) {\n\tcfg := &config.Config{\n\t\tSecrets:    config.Secrets{Keys: []string{\"thirty-two-byte-long-test-secret\"}},\n\t\tThirdParty: config.ThirdParty{ErrorRedirectURL: \"https://example.com/error\"},\n\t}\n\n\tstate := \"HmD7wlGQ7bF_4MGtmFRQuuSGTshHETDs4RQa64JAx-6EsmNsUjaQwYNOnjWUs6qIOuQMBTKapDGVXVCk00pX2vSS-x-WVqdzZ8KyeQ-9IHu2mwb-AeRbb2QPE-GFnvp2wrbCskKvWvtOfipyeTsnYY5iM90DxssaUtvKnawaB5_MNNekfKyiOeepIkKjUfSJ6-yTR7AAA4B9jwOfDRB4zdV8kKPVJlGVBJFosL11YWJaLxRGQR69nah3Jf9Z6bSAGXxWp24PoBYhij-dH4JyDCcU7D-NeT2A8qFFFjQ1m28C8fsr6zqb4w==\"\n\n\tvalidState, err := VerifyState(cfg, state, state)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, validState)\n\trequire.Equal(t, \"test-provider\", validState.Provider)\n\trequire.Equal(t, \"https://example.com\", validState.RedirectTo)\n\trequire.NotEmpty(t, validState.Nonce)\n\trequire.True(t, time.Now().UTC().Before(validState.ExpiresAt))\n}\nfunc TestThirdParty_VerifyState_Error(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\tstate         string\n\t\texpectedState string\n\t\texpectedError string\n\t}{\n\t\t{\n\t\t\tname:          \"error on invalid state\",\n\t\t\tstate:         \"invalid_state\",\n\t\t\texpectedError: \"could not decode state\",\n\t\t},\n\t\t{\n\t\t\tname:          \"error on invalid expected state\",\n\t\t\tstate:         \"ctj9hAU6kFkdc-g5ZXWGHbbnE1NSoQ55afdcYWLImTc5C5gGdeaknPDvZ560LJ07uA8I7X2ssPBKkkQb5xDnDHykmQtEp1hBp0uP_PdQdNJZ7FVXb0MpVWMrCv9nZp_fquLQvzjdm2nGRP9VKPt6S_7XNqg_mMA_ri9g7XWuNgJUqUE0PW7TGB9otsD2E7FQNowAl_vY-q0mYYIdkl0qb3lxWxnSzP51ewVv5VTeI7Mfr1gXyxWyTdqTnl_s5azaRq6w8J2SSX1_GTppX49zqCidPVIqYp6IyvotyOz-ePmvTg7tizBsaQ==\",\n\t\t\texpectedState: \"invalid_state\",\n\t\t\texpectedError: \"could not decode expectedState\",\n\t\t},\n\t\t{\n\t\t\tname:          \"error on nonce mismatch\",\n\t\t\tstate:         \"ctj9hAU6kFkdc-g5ZXWGHbbnE1NSoQ55afdcYWLImTc5C5gGdeaknPDvZ560LJ07uA8I7X2ssPBKkkQb5xDnDHykmQtEp1hBp0uP_PdQdNJZ7FVXb0MpVWMrCv9nZp_fquLQvzjdm2nGRP9VKPt6S_7XNqg_mMA_ri9g7XWuNgJUqUE0PW7TGB9otsD2E7FQNowAl_vY-q0mYYIdkl0qb3lxWxnSzP51ewVv5VTeI7Mfr1gXyxWyTdqTnl_s5azaRq6w8J2SSX1_GTppX49zqCidPVIqYp6IyvotyOz-ePmvTg7tizBsaQ==\",\n\t\t\texpectedState: \"Fs1SEytL1YuZJvBCyfF3Iydpl3-aC95bDsIb3VIcbq3sZjhn6iQaNFVNQZG1Maz2bI7Zkz3UvEIrIFohIotDqoR057mBnDKHVeg-TZKLXfviPJqfkOldfNPTpJgO9e4biYGxPx6qkmabk83eO77Qe3rJ2XM6FznQqPoMjq1vOBBJXjBUPeu3KtF1l7ONpHHVCV1Sr0cm0qQ4q5nPYgjI227AOa_MOOIDKfwqxb-jQT_9n0vLtVen9aYFbr_i_57r5aC3nCxnBgq7gXDq7z-E5NDRW23E0x-5CpQdDhElmtoeu4XRx1jOfw==\",\n\t\t\texpectedError: \"could not verify state\",\n\t\t},\n\t\t{\n\t\t\tname:          \"error on expired state\",\n\t\t\tstate:         \"UHPShpOq0byNI-A1vvUROLsPjNhGF5Xxdme4llZrnCvXfyZP_RnhRq490XPqKGKeWa621MtAUsV7N6C4OGx-EXK1TaLWNIYfFmByIEIlPSkpMVTFEUedY5UaFXwhWhuQb4Ci0r3QlPCLmQnPin1O4Vb59K2KieJwTvVnZzY3Y3Avj-D91acTMRGN6OabuIDDOH4nl0qDJABkZ6tnYk735ot8s3oyvJmZgmsW0qLOC_OoNMGZqLUTQzCrEayJ-gTXKr6HSvClN-4KGi4htHHLwYjrSCr_6tnQxDhZvNq7GKgXUEXfeUmZGQ==\",\n\t\t\texpectedState: \"UHPShpOq0byNI-A1vvUROLsPjNhGF5Xxdme4llZrnCvXfyZP_RnhRq490XPqKGKeWa621MtAUsV7N6C4OGx-EXK1TaLWNIYfFmByIEIlPSkpMVTFEUedY5UaFXwhWhuQb4Ci0r3QlPCLmQnPin1O4Vb59K2KieJwTvVnZzY3Y3Avj-D91acTMRGN6OabuIDDOH4nl0qDJABkZ6tnYk735ot8s3oyvJmZgmsW0qLOC_OoNMGZqLUTQzCrEayJ-gTXKr6HSvClN-4KGi4htHHLwYjrSCr_6tnQxDhZvNq7GKgXUEXfeUmZGQ==\",\n\t\t\texpectedError: \"state is expired\",\n\t\t},\n\t}\n\tfor _, testData := range tests {\n\t\tt.Run(testData.name, func(t *testing.T) {\n\t\t\tcfg := &config.Config{\n\t\t\t\tSecrets: config.Secrets{Keys: []string{\"thirty-two-byte-long-test-secret\"}},\n\t\t\t}\n\t\t\t_, err := VerifyState(cfg, testData.state, testData.expectedState)\n\t\t\tassert.NotNil(t, err)\n\t\t\tassert.True(t, strings.Contains(err.Error(), testData.expectedError))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/utils/cookie.go",
    "content": "package utils\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"net/http\"\n)\n\nconst (\n\tHankoThirdpartyStateCookie = \"hanko_thirdparty_state\"\n\tHankoTokenQuery            = \"hanko_token\"\n)\n\ntype CookieOptions struct {\n\tMaxAge   int\n\tPath     string\n\tSameSite http.SameSite\n}\n\nfunc GenerateStateCookie(config *config.Config, name string, state string, options CookieOptions) *http.Cookie {\n\tif options.Path == \"\" {\n\t\toptions.Path = \"/\"\n\t}\n\n\tif options.MaxAge == 0 {\n\t\toptions.MaxAge = 300\n\t}\n\n\treturn &http.Cookie{\n\t\tName:     name,\n\t\tValue:    state,\n\t\tPath:     options.Path,\n\t\tDomain:   config.Session.Cookie.Domain,\n\t\tMaxAge:   options.MaxAge,\n\t\tSecure:   config.Session.Cookie.Secure,\n\t\tHttpOnly: config.Session.Cookie.HttpOnly,\n\t\tSameSite: options.SameSite,\n\t}\n}\n"
  },
  {
    "path": "backend/utils/cookie_test.go",
    "content": "package utils\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"net/http\"\n\t\"testing\"\n)\n\nfunc TestThirdParty_GenerateCookie(t *testing.T) {\n\tcfg := &config.Config{\n\t\tSession: config.Session{\n\t\t\tCookie: config.Cookie{\n\t\t\t\tDomain:   \"lorem\",\n\t\t\t\tHttpOnly: true,\n\t\t\t\tSecure:   false,\n\t\t\t},\n\t\t},\n\t}\n\n\tcookieOptions := CookieOptions{\n\t\tMaxAge:   300,\n\t\tPath:     \"/lorem/cookie\",\n\t\tSameSite: http.SameSiteLaxMode,\n\t}\n\n\tstate := \"I am a test state\"\n\n\tcookie := GenerateStateCookie(cfg, HankoThirdpartyStateCookie, state, cookieOptions)\n\n\tassert.NotNil(t, cookie)\n\tassert.Equal(t, cookie.Name, HankoThirdpartyStateCookie)\n\tassert.Equal(t, cookie.Value, state)\n\tassert.Equal(t, cookie.Path, cookieOptions.Path)\n\tassert.Equal(t, cookie.Domain, cfg.Session.Cookie.Domain)\n\tassert.Equal(t, cookie.MaxAge, cookieOptions.MaxAge)\n\tassert.Equal(t, cookie.Secure, cfg.Session.Cookie.Secure)\n\tassert.Equal(t, cookie.HttpOnly, cfg.Session.Cookie.HttpOnly)\n\tassert.Equal(t, cookie.SameSite, cookieOptions.SameSite)\n}\n\nfunc TestThirdParty_GenerateCookieWithEmptyPath(t *testing.T) {\n\tcfg := &config.Config{\n\t\tSession: config.Session{\n\t\t\tCookie: config.Cookie{\n\t\t\t\tDomain:   \"lorem\",\n\t\t\t\tHttpOnly: true,\n\t\t\t\tSecure:   false,\n\t\t\t},\n\t\t},\n\t}\n\n\tcookieOptions := CookieOptions{\n\t\tMaxAge:   300,\n\t\tSameSite: http.SameSiteLaxMode,\n\t}\n\n\tstate := \"I am a test state\"\n\n\tcookie := GenerateStateCookie(cfg, HankoThirdpartyStateCookie, state, cookieOptions)\n\n\tassert.Equal(t, cookie.Path, \"/\")\n}\n\nfunc TestThirdParty_GenerateCookieWithEmptyMaxAge(t *testing.T) {\n\tcfg := &config.Config{\n\t\tSession: config.Session{\n\t\t\tCookie: config.Cookie{\n\t\t\t\tDomain:   \"lorem\",\n\t\t\t\tHttpOnly: true,\n\t\t\t\tSecure:   false,\n\t\t\t},\n\t\t},\n\t}\n\n\tcookieOptions := CookieOptions{\n\t\tMaxAge:   0,\n\t\tPath:     \"/lorem\",\n\t\tSameSite: http.SameSiteLaxMode,\n\t}\n\n\tstate := \"I am a test state\"\n\n\tcookie := GenerateStateCookie(cfg, HankoThirdpartyStateCookie, state, cookieOptions)\n\n\tassert.Equal(t, cookie.MaxAge, 300)\n}\n"
  },
  {
    "path": "backend/utils/mask.go",
    "content": "package utils\n\nimport \"strings\"\n\nvar mask = \"*\"\n\nfunc MaskEmail(email string) string {\n\tif len(email) == 0 {\n\t\treturn \"\"\n\t}\n\n\ttmp := strings.Split(email, \"@\")\n\n\tname := tmp[0]\n\tdomain := tmp[1]\n\n\tnameRunes := []rune(name)\n\tnameRunesLen := len(nameRunes)\n\n\tif nameRunesLen == 0 {\n\t\treturn strings.Repeat(mask, 6) + \"@\" + domain\n\t}\n\n\tvar maskStart, padLength int\n\tif nameRunesLen <= 6 {\n\t\tmaskStart = 1\n\t\tpadLength = 6 - nameRunesLen\n\t} else {\n\t\tmaskStart = 3\n\n\t}\n\n\tmaskedAddress := \"\"\n\tmaskedAddress += string(nameRunes[:maskStart])\n\tmaskedAddress += strings.Repeat(mask, len(nameRunes[maskStart:])+padLength)\n\tmaskedAddress += \"@\" + domain\n\n\treturn maskedAddress\n}\n\nfunc MaskUsername(username string) string {\n\tusernameRunes := []rune(username)\n\tusernameRunesLen := len(usernameRunes)\n\n\tif usernameRunesLen == 0 {\n\t\treturn \"\"\n\t}\n\n\tif usernameRunesLen == 1 {\n\t\treturn mask\n\t}\n\n\tpadLength := 0\n\tif usernameRunesLen <= 3 {\n\t\tpadLength = 6 - usernameRunesLen\n\t}\n\n\tmaskedUsername := \"\"\n\tmaskedUsername += string(usernameRunes[0])\n\tmaskedUsername += strings.Repeat(mask, usernameRunesLen-2+padLength)\n\tmaskedUsername += string(usernameRunes[usernameRunesLen-1])\n\n\treturn maskedUsername\n}\n"
  },
  {
    "path": "backend/utils/mask_test.go",
    "content": "package utils\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestMaskEmail(t *testing.T) {\n\ttype args struct {\n\t\temail string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"return empty string on empty email address\",\n\t\t\targs: args{\"\"},\n\t\t\twant: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty name part\",\n\t\t\targs: args{\"@domain.com\"},\n\t\t\twant: \"******@domain.com\",\n\t\t},\n\t\t{\n\t\t\tname: \"mask start reduced and padding applied when name length < 6\",\n\t\t\targs: args{\"123@domain.com\"},\n\t\t\twant: \"1*****@domain.com\",\n\t\t},\n\t\t{\n\t\t\tname: \"start mask at index 4 and mask everything until '@' rune\",\n\t\t\targs: args{\"really_long_test_email_help_when_does_it_stop@domain.com\"},\n\t\t\twant: \"rea******************************************@domain.com\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equalf(t, tt.want, MaskEmail(tt.args.email), \"MaskEmail(%v)\", tt.args.email)\n\t\t})\n\t}\n}\n\nfunc TestMaskUsername(t *testing.T) {\n\ttype args struct {\n\t\tusername string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"return empty string on empty username\",\n\t\t\targs: args{username: \"\"},\n\t\t\twant: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"mask everything if username length == 1\",\n\t\t\targs: args{username: \"X\"},\n\t\t\twant: \"*\",\n\t\t},\n\t\t{\n\t\t\tname: \"mask and pad when username length 2 or 3\",\n\t\t\targs: args{username: \"xx\"},\n\t\t\twant: \"x****x\",\n\t\t},\n\t\t{\n\t\t\tname: \"mask everything but first and last rune when username size > 3\",\n\t\t\targs: args{username: \"test_username\"},\n\t\t\twant: \"t***********e\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equalf(t, tt.want, MaskUsername(tt.args.username), \"MaskUsername(%v)\", tt.args.username)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/utils/url.go",
    "content": "package utils\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n)\n\nconst (\n\tpictureURLReasonEmpty         = \"empty\"\n\tpictureURLReasonTooLong       = \"too_long\"\n\tpictureURLReasonInvalidURI    = \"invalid_uri\"\n\tpictureURLReasonInvalidScheme = \"invalid_scheme\"\n\tpictureURLReasonMissingHost   = \"missing_host\"\n\tpictureURLReasonHasUserInfo   = \"has_userinfo\"\n)\n\n// PictureURLError is returned by ValidatePictureURL when the URL is invalid.\ntype PictureURLError struct {\n\tReason string\n}\n\nfunc (e PictureURLError) Error() string {\n\treturn fmt.Sprintf(\"invalid picture url: %s\", e.Reason)\n}\n\n// ValidatePictureURL returns nil if valid, otherwise an error that includes a reason string.\nfunc ValidatePictureURL(raw string) error {\n\traw = strings.TrimSpace(raw)\n\tif raw == \"\" {\n\t\treturn PictureURLError{Reason: pictureURLReasonEmpty}\n\t}\n\tif len(raw) > 2048 {\n\t\treturn PictureURLError{Reason: pictureURLReasonTooLong}\n\t}\n\n\tu, err := url.ParseRequestURI(raw)\n\tif err != nil {\n\t\treturn PictureURLError{Reason: pictureURLReasonInvalidURI}\n\t}\n\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn PictureURLError{Reason: pictureURLReasonInvalidScheme}\n\t}\n\n\tif u.Host == \"\" {\n\t\treturn PictureURLError{Reason: pictureURLReasonMissingHost}\n\t}\n\n\t// Disallow credentials in URL (user:pass@host).\n\tif u.User != nil {\n\t\treturn PictureURLError{Reason: pictureURLReasonHasUserInfo}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/utils/url_test.go",
    "content": "package utils\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestValidatePictureURL(t *testing.T) {\n\ttests := []struct {\n\t\tname       string\n\t\traw        string\n\t\twantReason string\n\t}{\n\t\t{\n\t\t\tname:       \"valid https URL\",\n\t\t\traw:        \"https://example.com/image.png\",\n\t\t\twantReason: \"\",\n\t\t},\n\t\t{\n\t\t\tname:       \"valid http URL\",\n\t\t\traw:        \"http://example.com/image.png\",\n\t\t\twantReason: \"\",\n\t\t},\n\t\t{\n\t\t\tname:       \"valid URL with query\",\n\t\t\traw:        \"https://example.com/image.png?size=large\",\n\t\t\twantReason: \"\",\n\t\t},\n\t\t{\n\t\t\tname:       \"empty string\",\n\t\t\traw:        \"\",\n\t\t\twantReason: pictureURLReasonEmpty,\n\t\t},\n\t\t{\n\t\t\tname:       \"only whitespace\",\n\t\t\traw:        \"   \",\n\t\t\twantReason: pictureURLReasonEmpty,\n\t\t},\n\t\t{\n\t\t\tname:       \"too long URL\",\n\t\t\traw:        \"https://example.com/\" + strings.Repeat(\"a\", 2048),\n\t\t\twantReason: pictureURLReasonTooLong,\n\t\t},\n\t\t{\n\t\t\tname:       \"invalid URI\",\n\t\t\traw:        \"://example.com\",\n\t\t\twantReason: pictureURLReasonInvalidURI,\n\t\t},\n\t\t{\n\t\t\tname:       \"invalid scheme (ftp)\",\n\t\t\traw:        \"ftp://example.com/image.png\",\n\t\t\twantReason: pictureURLReasonInvalidScheme,\n\t\t},\n\t\t{\n\t\t\tname:       \"invalid scheme (data)\",\n\t\t\traw:        \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==\",\n\t\t\twantReason: pictureURLReasonInvalidScheme,\n\t\t},\n\t\t{\n\t\t\tname:       \"missing host\",\n\t\t\traw:        \"https:///path\",\n\t\t\twantReason: pictureURLReasonMissingHost,\n\t\t},\n\t\t{\n\t\t\tname:       \"has user info\",\n\t\t\traw:        \"https://user:pass@example.com/image.png\",\n\t\t\twantReason: pictureURLReasonHasUserInfo,\n\t\t},\n\t\t{\n\t\t\tname:       \"valid URL with spaces\",\n\t\t\traw:        \"  https://example.com/image.png  \",\n\t\t\twantReason: \"\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := ValidatePictureURL(tt.raw)\n\n\t\t\tif tt.wantReason == \"\" {\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"ValidatePictureURL(%q) error = %v, want nil\", tt.raw, err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif err == nil {\n\t\t\t\tt.Fatalf(\"ValidatePictureURL(%q) error = nil, want reason %q\", tt.raw, tt.wantReason)\n\t\t\t}\n\n\t\t\tperr, ok := err.(PictureURLError)\n\t\t\tif !ok {\n\t\t\t\tt.Fatalf(\"ValidatePictureURL(%q) error type = %T, want utils.PictureURLError\", tt.raw, err)\n\t\t\t}\n\n\t\t\tif perr.Reason != tt.wantReason {\n\t\t\t\tt.Fatalf(\"ValidatePictureURL(%q) reason = %q, want %q\", tt.raw, perr.Reason, tt.wantReason)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "backend/webhooks/config_hook.go",
    "content": "package webhooks\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"time\"\n)\n\ntype ConfigHook struct {\n\tBaseWebhook\n}\n\nfunc NewConfigHook(cfgHook config.Webhook, logger echo.Logger) Webhook {\n\treturn &ConfigHook{\n\t\tBaseWebhook{\n\t\t\tLogger:   logger,\n\t\t\tCallback: cfgHook.Callback,\n\t\t\tEvents:   cfgHook.Events,\n\t\t},\n\t}\n}\n\nfunc (ch *ConfigHook) DisableOnExpiryDate(_ time.Time) error {\n\treturn nil\n}\n\nfunc (ch *ConfigHook) DisableOnFailure() error {\n\treturn nil\n}\n\nfunc (ch *ConfigHook) Reset() error {\n\treturn nil\n}\n\nfunc (ch *ConfigHook) IsEnabled() bool {\n\treturn true\n}\n"
  },
  {
    "path": "backend/webhooks/config_hook_test.go",
    "content": "package webhooks\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestNewConfigHook(t *testing.T) {\n\thook := config.Webhook{\n\t\tCallback: \"http://lorem.ipsum\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tcfgHook := NewConfigHook(hook, nil)\n\trequire.NotEmpty(t, cfgHook)\n}\n\nfunc TestConfigHook_DisableOnExpiryDate(t *testing.T) {\n\tnow := time.Now()\n\thook := config.Webhook{\n\t\tCallback: \"http://lorem.ipsum\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdbHook := NewConfigHook(hook, nil)\n\terr := dbHook.DisableOnExpiryDate(now)\n\tassert.NoError(t, err)\n}\n\nfunc TestConfigHook_DisableOnFailure(t *testing.T) {\n\thook := config.Webhook{\n\t\tCallback: \"http://lorem.ipsum\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdbHook := NewConfigHook(hook, nil)\n\terr := dbHook.DisableOnFailure()\n\tassert.NoError(t, err)\n}\n\nfunc TestConfigHook_Reset(t *testing.T) {\n\thook := config.Webhook{\n\t\tCallback: \"http://lorem.ipsum\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdbHook := NewConfigHook(hook, nil)\n\terr := dbHook.Reset()\n\tassert.NoError(t, err)\n}\n\nfunc TestConfigHook_IsEnabled(t *testing.T) {\n\thook := config.Webhook{\n\t\tCallback: \"http://lorem.ipsum\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdbHook := NewConfigHook(hook, nil)\n\tisEnabled := dbHook.IsEnabled()\n\trequire.True(t, isEnabled)\n}\n"
  },
  {
    "path": "backend/webhooks/database_hook.go",
    "content": "package webhooks\n\nimport (\n\t\"fmt\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"time\"\n)\n\nconst (\n\tFailureExpireRate = 5\n)\n\ntype DatabaseHook struct {\n\tBaseWebhook\n\tpersister persistence.WebhookPersister\n\trawHook   models.Webhook\n}\n\nfunc NewDatabaseHook(dbHook models.Webhook, persister persistence.WebhookPersister, logger echo.Logger) Webhook {\n\treturn &DatabaseHook{\n\t\tBaseWebhook{\n\t\t\tLogger:   logger,\n\t\t\tCallback: dbHook.Callback,\n\t\t\tEvents:   events.ConvertFromDbList(dbHook.WebhookEvents),\n\t\t},\n\t\tpersister,\n\t\tdbHook,\n\t}\n}\n\nfunc (dh *DatabaseHook) DisableOnExpiryDate(now time.Time) error {\n\t// check expire date\n\tif dh.rawHook.ExpiresAt.Before(now) {\n\t\tdh.rawHook.Enabled = false\n\n\t\terr := dh.persister.Update(dh.rawHook)\n\t\tif err != nil {\n\t\t\tdh.Logger.Error(fmt.Errorf(\"unable to expire webhook on date: %w\", err))\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (dh *DatabaseHook) DisableOnFailure() error {\n\t// increase Failure-Rate\n\tdh.rawHook.Failures++\n\n\tif dh.rawHook.Failures > FailureExpireRate {\n\t\tdh.rawHook.Enabled = false\n\t}\n\n\terr := dh.persister.Update(dh.rawHook)\n\tif err != nil {\n\t\tdh.Logger.Error(fmt.Errorf(\"unable to expire webhook on failure: %w\", err))\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (dh *DatabaseHook) Reset() error {\n\tnow := time.Now()\n\tdh.rawHook.Failures = 0\n\tdh.rawHook.ExpiresAt = now.Add(WebhookExpireDuration)\n\tdh.rawHook.UpdatedAt = now\n\n\terr := dh.persister.Update(dh.rawHook)\n\tif err != nil {\n\t\tdh.Logger.Error(fmt.Errorf(\"unable to reset webhook failures: %w\", err))\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (dh *DatabaseHook) IsEnabled() bool {\n\treturn dh.rawHook.Enabled\n}\n"
  },
  {
    "path": "backend/webhooks/database_hook_test.go",
    "content": "package webhooks\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n)\n\nfunc TestDatabaseHookSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(databaseHookSuite))\n}\n\ntype databaseHookSuite struct {\n\ttest.Suite\n}\n\nfunc (s *databaseHookSuite) TestNewDatabaseHook() {\n\thookId, err := uuid.NewV4()\n\ts.Require().NoError(err)\n\n\thook := models.Webhook{\n\t\tID:        hookId,\n\t\tEnabled:   false,\n\t\tFailures:  0,\n\t\tExpiresAt: time.Now().Add(WebhookExpireDuration),\n\t}\n\n\tdbHook := NewDatabaseHook(hook, s.Storage.GetWebhookPersister(nil), nil)\n\ts.NotEmpty(dbHook)\n}\n\nfunc (s *databaseHookSuite) TestDatabaseHook_DisableOnExpiryDate() {\n\thook, whPersister := s.loadWebhook(\"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da3\")\n\tdbHook := NewDatabaseHook(hook, whPersister, nil)\n\n\tnow := time.Now()\n\terr := dbHook.DisableOnExpiryDate(now)\n\ts.NoError(err)\n\n\tupdatedHook, err := whPersister.Get(hook.ID)\n\ts.Require().NoError(err)\n\n\ts.False(updatedHook.Enabled)\n}\nfunc (s *databaseHookSuite) TestDatabaseHook_DoNotDisableOnExpiryDate() {\n\thook, whPersister := s.loadWebhook(\"a47fe92a-1e4b-4119-8653-55ad82737c88\")\n\n\tdbHook := NewDatabaseHook(hook, whPersister, nil)\n\n\tnow := time.Now()\n\terr := dbHook.DisableOnExpiryDate(now)\n\ts.NoError(err)\n\n\tupdatedHook, err := whPersister.Get(hook.ID)\n\ts.Require().NoError(err)\n\n\ts.True(updatedHook.Enabled)\n}\n\nfunc (s *databaseHookSuite) TestDatabaseHook_DisableOnFailure() {\n\thook, whPersister := s.loadWebhook(\"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da2\")\n\n\tdbHook := NewDatabaseHook(hook, whPersister, nil)\n\terr := dbHook.DisableOnFailure()\n\ts.Require().NoError(err)\n\n\tupdatedHook, err := whPersister.Get(hook.ID)\n\ts.NoError(err)\n\n\ts.False(updatedHook.Enabled)\n}\n\nfunc (s *databaseHookSuite) TestDatabaseHook_DoNotDisableOnFailure() {\n\thook, whPersister := s.loadWebhook(\"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da3\")\n\n\tdbHook := NewDatabaseHook(hook, whPersister, nil)\n\terr := dbHook.DisableOnFailure()\n\ts.NoError(err)\n\n\tupdatedHook, err := whPersister.Get(hook.ID)\n\ts.Require().NoError(err)\n\n\ts.True(updatedHook.Enabled)\n}\n\nfunc (s *databaseHookSuite) TestDatabaseHook_Reset() {\n\thook, whPersister := s.loadWebhook(\"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da2\")\n\n\tdbHook := NewDatabaseHook(hook, whPersister, nil)\n\terr := dbHook.Reset()\n\ts.NoError(err)\n\n\tupdatedHook, err := whPersister.Get(hook.ID)\n\ts.Require().NoError(err)\n\n\t// Failures should be reset\n\ts.Equal(0, updatedHook.Failures)\n\ts.Less(updatedHook.Failures, hook.Failures, \"Failures should be reset to 0\")\n\n\t// Ensure timestamps moved forward relative to the original hook values\n\ts.True(updatedHook.UpdatedAt.After(hook.UpdatedAt), \"UpdatedAt should be updated\")\n\ts.True(updatedHook.ExpiresAt.After(hook.ExpiresAt), \"ExpiresAt should be updated\")\n}\n\nfunc (s *databaseHookSuite) TestDatabaseHook_IsEnabled() {\n\thook, whPersister := s.loadWebhook(\"a47fe92a-1e4b-4119-8653-55ad82737c88\")\n\n\tdbHook := NewDatabaseHook(hook, whPersister, nil)\n\n\ts.True(dbHook.IsEnabled())\n}\n\nfunc (s *databaseHookSuite) TestDatabaseHook_IsDisabled() {\n\thook, whPersister := s.loadWebhook(\"279beae1-8a6d-4eaf-a791-1fa79d21d37a\")\n\n\tdbHook := NewDatabaseHook(hook, whPersister, nil)\n\n\ts.False(dbHook.IsEnabled())\n}\n\nfunc (s *databaseHookSuite) loadWebhook(hookId string) (models.Webhook, persistence.WebhookPersister) {\n\terr := s.LoadFixtures(\"../test/fixtures/webhooks\")\n\ts.Require().NoError(err)\n\n\twhPersister := s.Storage.GetWebhookPersister(nil)\n\thook, err := whPersister.Get(uuid.FromStringOrNil(hookId))\n\ts.Require().NoError(err)\n\ts.Require().NotEmpty(hook)\n\n\treturn *hook, whPersister\n}\n"
  },
  {
    "path": "backend/webhooks/events/events.go",
    "content": "package events\n\nimport \"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\ntype Event string\n\nconst (\n\tUser               Event = \"user\"\n\tUserLogin          Event = \"user.login\"\n\tUserCreate         Event = \"user.create\"\n\tUserUpdate         Event = \"user.update\"\n\tUserDelete         Event = \"user.delete\"\n\tUserEmail          Event = \"user.update.email\"\n\tUserEmailCreate    Event = \"user.update.email.create\"\n\tUserEmailPrimary   Event = \"user.update.email.primary\"\n\tUserEmailDelete    Event = \"user.update.email.delete\"\n\tUserUsername       Event = \"user.update.username\"\n\tUserUsernameCreate Event = \"user.update.username.create\"\n\tUserUsernameDelete Event = \"user.update.username.delete\"\n\tUserUsernameUpdate Event = \"user.update.username.update\"\n\tUserPasswordChange Event = \"user.update.password.update\"\n\n\tEmailSend Event = \"email.send\"\n)\n\nfunc StringIsValidEvent(value string) bool {\n\tevt := Event(value)\n\treturn IsValidEvent(evt)\n}\n\nfunc IsValidEvent(evt Event) bool {\n\tvar isValid bool\n\tswitch evt {\n\tcase User, UserLogin, UserCreate, UserUpdate, UserDelete, UserEmail, UserEmailCreate, UserEmailPrimary, UserEmailDelete, UserUsername, UserUsernameCreate, UserUsernameUpdate, UserUsernameDelete, UserPasswordChange, EmailSend:\n\t\tisValid = true\n\tdefault:\n\t\tisValid = false\n\t}\n\n\treturn isValid\n}\n\ntype Events []Event\n\nfunc ConvertFromDbList(events models.WebhookEvents) Events {\n\tevts := make(Events, 0)\n\tfor _, event := range events {\n\t\tevts = append(evts, Event(event.Event))\n\t}\n\n\treturn evts\n}\n"
  },
  {
    "path": "backend/webhooks/manager.go",
    "content": "package webhooks\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/jwk\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n)\n\ntype Manager interface {\n\tTrigger(tx *pop.Connection, evt events.Event, data interface{})\n\tGenerateJWT(data interface{}, event events.Event) (string, error)\n}\n\ntype manager struct {\n\tlogger          echo.Logger\n\twebhooks        Webhooks\n\tjwtGenerator    jwk.Generator\n\taudience        []string\n\tpersister       persistence.Persister\n\tcanExpireAtTime bool\n}\n\nfunc NewManager(cfg *config.Config, persister persistence.Persister, jwtGenerator jwk.Generator, logger echo.Logger) (Manager, error) {\n\thooks := make(Webhooks, 0)\n\n\tif cfg.Webhooks.Enabled {\n\t\tfor _, cfgHook := range cfg.Webhooks.Hooks {\n\t\t\thooks = append(hooks, NewConfigHook(cfgHook, logger))\n\t\t}\n\t}\n\n\tvar audience []string\n\tif cfg.Session.Audience != nil && len(cfg.Session.Audience) > 0 {\n\t\taudience = cfg.Session.Audience\n\t} else {\n\t\taudience = []string{cfg.Webauthn.RelyingParty.Id}\n\t}\n\n\treturn &manager{\n\t\tlogger:          logger,\n\t\twebhooks:        hooks,\n\t\tjwtGenerator:    jwtGenerator,\n\t\taudience:        audience,\n\t\tpersister:       persister,\n\t\tcanExpireAtTime: cfg.Webhooks.AllowTimeExpiration,\n\t}, nil\n}\n\nfunc (m *manager) Trigger(tx *pop.Connection, evt events.Event, data interface{}) {\n\t// add db hooks - Done here to prevent a restart in case a hook is added or removed from the database\n\tdbHooks, err := m.persister.GetWebhookPersister(tx).List(false)\n\tif err != nil {\n\t\tm.logger.Error(fmt.Errorf(\"unable to get database webhooks: %w\", err))\n\t\treturn\n\t}\n\n\thooks := m.webhooks\n\tfor _, dbHook := range dbHooks {\n\t\thooks = append(hooks, NewDatabaseHook(dbHook, m.persister.GetWebhookPersister(nil), m.logger))\n\t}\n\n\tdataToken, err := m.GenerateJWT(data, evt)\n\tif err != nil {\n\t\tm.logger.Error(fmt.Errorf(\"unable to generate JWT for webhook data: %w\", err))\n\t\treturn\n\t}\n\n\tjobData := JobData{\n\t\tToken: dataToken,\n\t\tEvent: evt,\n\t}\n\n\thookChannel := make(chan Job, len(hooks))\n\tfor _, hook := range hooks {\n\t\tif hook.HasEvent(evt) {\n\t\t\tjob := Job{\n\t\t\t\tData:            jobData,\n\t\t\t\tHook:            hook,\n\t\t\t\tCanExpireAtTime: m.canExpireAtTime,\n\t\t\t}\n\t\t\thookChannel <- job\n\t\t}\n\t}\n\tclose(hookChannel)\n\n\tworker := NewWorker(hookChannel, m.logger)\n\tgo worker.Run()\n}\n\nfunc (m *manager) GenerateJWT(data interface{}, event events.Event) (string, error) {\n\tissuedAt := time.Now()\n\texpiration := issuedAt.Add(5 * time.Minute)\n\n\ttoken := jwt.New()\n\t_ = token.Set(jwt.SubjectKey, \"hanko webhooks\")\n\t_ = token.Set(jwt.IssuedAtKey, issuedAt)\n\t_ = token.Set(jwt.ExpirationKey, expiration)\n\t_ = token.Set(jwt.AudienceKey, m.audience)\n\t_ = token.Set(\"data\", data)\n\t_ = token.Set(\"evt\", event)\n\n\tsigned, err := m.jwtGenerator.Sign(token)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn string(signed), nil\n}\n"
  },
  {
    "path": "backend/webhooks/manager_test.go",
    "content": "package webhooks\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"github.com/teamhanko/hanko/backend/v2/test\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestManagerSuite(t *testing.T) {\n\tt.Parallel()\n\tsuite.Run(t, new(managerSuite))\n}\n\ntype managerSuite struct {\n\ttest.Suite\n}\n\nfunc (s *managerSuite) TestNewManager() {\n\tcfg := config.Config{}\n\tjwkManager := test.JwkManager{}\n\n\tmanager, err := NewManager(&cfg, s.Storage, jwkManager, nil)\n\ts.NoError(err)\n\ts.NotEmpty(manager)\n}\n\nfunc (s *managerSuite) TestManager_GenerateJWT() {\n\tcfg := config.Config{}\n\tjwkManager := test.JwkManager{}\n\n\tmanager, err := NewManager(&cfg, s.Storage, jwkManager, nil)\n\n\ttestData := \"lorem-ipsum\"\n\n\tdataToken, err := manager.GenerateJWT(testData, events.UserCreate)\n\ts.NoError(err)\n\ts.NotEmpty(dataToken)\n}\n\nfunc (s *managerSuite) TestManager_TriggerWithoutHook() {\n\ttriggered := false\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttriggered = true\n\t}))\n\tdefer server.Close()\n\n\tcfg := config.Config{}\n\tjwkManager := test.JwkManager{}\n\n\tmanager, err := NewManager(&cfg, s.Storage, jwkManager, nil)\n\ts.Require().NoError(err)\n\n\tmanager.Trigger(s.Storage.GetConnection(), events.UserCreate, \"lorem-ipsum\")\n\n\t// give it 1 sec to trigger\n\ttime.Sleep(1 * time.Second)\n\n\ts.False(triggered)\n}\nfunc (s *managerSuite) TestManager_TriggerWithConfigHook() {\n\ttriggered := false\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttriggered = true\n\t}))\n\tdefer server.Close()\n\n\thooks := config.Webhooks{config.Webhook{\n\t\tCallback: server.URL,\n\t\tEvents: events.Events{\n\t\t\tevents.UserCreate,\n\t\t},\n\t}}\n\n\tcfg := config.Config{\n\t\tWebhooks: config.WebhookSettings{\n\t\t\tEnabled: true,\n\t\t\tHooks:   hooks,\n\t\t},\n\t}\n\n\tjwkManager := test.JwkManager{}\n\tmanager, err := NewManager(&cfg, s.Storage, jwkManager, nil)\n\ts.Require().NoError(err)\n\n\tmanager.Trigger(s.Storage.GetConnection(), events.UserCreate, \"lorem-ipsum\")\n\n\t// give it 1 sec to trigger\n\ttime.Sleep(1 * time.Second)\n\n\ts.True(triggered)\n}\n\nfunc (s *managerSuite) TestManager_TriggerWithDisabledConfigHook() {\n\ttriggered := false\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttriggered = true\n\t}))\n\tdefer server.Close()\n\n\thooks := config.Webhooks{config.Webhook{\n\t\tCallback: server.URL,\n\t\tEvents: events.Events{\n\t\t\tevents.UserCreate,\n\t\t},\n\t}}\n\n\tcfg := config.Config{\n\t\tWebhooks: config.WebhookSettings{\n\t\t\tEnabled: false,\n\t\t\tHooks:   hooks,\n\t\t},\n\t}\n\n\tjwkManager := test.JwkManager{}\n\tmanager, err := NewManager(&cfg, s.Storage, jwkManager, nil)\n\ts.Require().NoError(err)\n\n\tmanager.Trigger(s.Storage.GetConnection(), events.UserCreate, \"lorem-ipsum\")\n\n\t// give it 1 sec to trigger\n\ttime.Sleep(1 * time.Second)\n\n\ts.False(triggered)\n}\n\nfunc (s *managerSuite) TestManager_TriggerWithDbHook() {\n\ttriggered := false\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttriggered = true\n\t}))\n\tdefer server.Close()\n\n\tcfg := config.Config{}\n\tjwkManager := test.JwkManager{}\n\n\tpersister := s.Storage.GetWebhookPersister(nil)\n\n\ts.createTestDatabaseWebhook(persister, true, server.URL)\n\n\tmanager, err := NewManager(&cfg, s.Storage, jwkManager, nil)\n\ts.Require().NoError(err)\n\n\tmanager.Trigger(s.Storage.GetConnection(), events.UserCreate, \"lorem-ipsum\")\n\n\t// give it 1 sec to trigger\n\ttime.Sleep(1 * time.Second)\n\n\ts.True(triggered)\n}\n\nfunc (s *managerSuite) TestManager_TriggerWithDisabledDbHook() {\n\ttriggered := false\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttriggered = true\n\t}))\n\tdefer server.Close()\n\n\tcfg := config.Config{}\n\tjwkManager := test.JwkManager{}\n\tpersister := s.Storage.GetWebhookPersister(nil)\n\n\ts.createTestDatabaseWebhook(persister, false, server.URL)\n\n\tmanager, err := NewManager(&cfg, s.Storage, jwkManager, nil)\n\ts.Require().NoError(err)\n\n\tmanager.Trigger(s.Storage.GetConnection(), events.UserCreate, \"lorem-ipsum\")\n\n\t// give it 1 sec to trigger\n\ttime.Sleep(1 * time.Second)\n\n\ts.False(triggered)\n}\n\nfunc (s *managerSuite) createTestDatabaseWebhook(persister persistence.WebhookPersister, isEnabled bool, callback string) {\n\tnow := time.Now()\n\thookId := uuid.FromStringOrNil(\"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da1\")\n\terr := persister.Create(\n\t\tmodels.Webhook{\n\t\t\tID:        hookId,\n\t\t\tCallback:  callback,\n\t\t\tEnabled:   isEnabled,\n\t\t\tFailures:  0,\n\t\t\tExpiresAt: now.Add(WebhookExpireDuration),\n\t\t\tCreatedAt: now,\n\t\t\tUpdatedAt: now,\n\t\t},\n\t\tmodels.WebhookEvents{\n\t\t\tmodels.WebhookEvent{\n\t\t\t\tID:        uuid.FromStringOrNil(\"8b00da9a-cacf-45ea-b25d-c1ce0f0d7da0\"),\n\t\t\t\tWebhookID: hookId,\n\t\t\t\tEvent:     string(events.UserCreate),\n\t\t\t\tCreatedAt: now,\n\t\t\t\tUpdatedAt: now,\n\t\t\t},\n\t\t})\n\ts.Require().NoError(err)\n}\n"
  },
  {
    "path": "backend/webhooks/utils/webhook.go",
    "content": "package utils\n\nimport (\n\t\"fmt\"\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto/admin\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n)\n\nfunc TriggerWebhooks(ctx echo.Context, tx *pop.Connection, evt events.Event, data interface{}) error {\n\twebhookCtx := ctx.Get(\"webhook_manager\")\n\tif webhookCtx == nil {\n\t\treturn fmt.Errorf(\"unable to load webhooks manager from webhook middleware\")\n\t}\n\n\twebhookManager := webhookCtx.(webhooks.Manager)\n\twebhookManager.Trigger(tx, evt, data)\n\n\treturn nil\n}\n\nfunc NotifyUserChange(ctx echo.Context, tx *pop.Connection, persister persistence.Persister, event events.Event, userId uuid.UUID) {\n\tupdatedUser, err := persister.GetUserPersisterWithConnection(tx).Get(userId)\n\tif err != nil {\n\t\tctx.Logger().Warn(fmt.Errorf(\"failed to fetch updated user: %w\", err))\n\t\treturn\n\t}\n\n\tuser := admin.FromUserModel(*updatedUser)\n\tuser.SetUserAgent(ctx.Request().UserAgent())\n\tuser.SetIPAddress(ctx.RealIP())\n\n\terr = TriggerWebhooks(ctx, tx, event, user)\n\tif err != nil {\n\t\tctx.Logger().Warn(err)\n\t}\n}\n"
  },
  {
    "path": "backend/webhooks/utils/webhook_test.go",
    "content": "package utils\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype testManager struct {\n\tTestFunc func()\n}\n\nfunc (tm *testManager) Trigger(tx *pop.Connection, evt events.Event, data interface{}) {\n\ttm.TestFunc()\n}\n\nfunc (tm *testManager) GenerateJWT(data interface{}, event events.Event) (string, error) {\n\treturn \"\", nil\n}\n\nfunc TestWebhook_TriggerWithoutManager(t *testing.T) {\n\te := echo.New()\n\treq := httptest.NewRequest(http.MethodGet, \"/path\", nil)\n\trec := httptest.NewRecorder()\n\n\tctx := e.NewContext(req, rec)\n\n\terr := TriggerWebhooks(ctx, nil, \"user\", \"lorem\")\n\trequire.Error(t, err)\n\n\terr = e.Close()\n\trequire.NoError(t, err)\n}\n\nfunc TestWebhook_Trigger(t *testing.T) {\n\te := echo.New()\n\treq := httptest.NewRequest(http.MethodGet, \"/path\", nil)\n\trec := httptest.NewRecorder()\n\n\ttm := &testManager{TestFunc: func() {\n\t\trequire.True(t, true)\n\t}}\n\n\tctx := e.NewContext(req, rec)\n\tctx.Set(\"webhook_manager\", tm)\n\n\terr := TriggerWebhooks(ctx, nil, \"user\", \"lorem\")\n\trequire.NoError(t, err)\n\n\terr = e.Close()\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "backend/webhooks/webhook.go",
    "content": "package webhooks\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype Webhook interface {\n\tTrigger(data JobData) error\n\tDisableOnExpiryDate(now time.Time) error\n\tDisableOnFailure() error\n\tReset() error\n\tIsEnabled() bool\n\tHasEvent(evt events.Event) bool\n}\n\ntype Webhooks []Webhook\n\nconst (\n\tWebhookExpireDuration = 30 * 24 * time.Hour // 30 Days\n)\n\ntype BaseWebhook struct {\n\tLogger   echo.Logger\n\tCallback string\n\tEvents   events.Events\n\tTimeout  time.Duration\n}\n\nfunc (bh *BaseWebhook) HasEvent(evt events.Event) bool {\n\tfor _, event := range bh.Events {\n\t\tif strings.HasPrefix(string(evt), string(event)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (bh *BaseWebhook) Trigger(data JobData) error {\n\t// create request\n\tdataJson, err := json.Marshal(data)\n\tif err != nil {\n\t\tbh.Logger.Error(fmt.Errorf(\"unable to convert JobData to json: %w\", err))\n\t\treturn err\n\t}\n\n\trequest, err := http.NewRequest(http.MethodPost, bh.Callback, bytes.NewReader(dataJson))\n\tif err != nil {\n\t\tbh.Logger.Error(fmt.Errorf(\"unable to create request for webhook: %w\", err))\n\t\treturn err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\n\ttimeout := bh.Timeout\n\tif timeout == 0 {\n\t\ttimeout = 10 * time.Second\n\t}\n\n\tclient := http.Client{\n\t\tTimeout: timeout,\n\t}\n\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\tbh.Logger.Error(fmt.Errorf(\"unable to execute webhook request: %w\", err))\n\t\treturn err\n\t}\n\n\tdefer func() {\n\t\tif err := response.Body.Close(); err != nil {\n\t\t\tbh.Logger.Error(fmt.Errorf(\"failed to close webhook response body: %w\", err))\n\t\t}\n\t}()\n\n\tif response.StatusCode >= http.StatusBadRequest {\n\t\terr := fmt.Errorf(\"request failed due to status code: %d\", response.StatusCode)\n\t\tbh.Logger.Error(err)\n\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/webhooks/webhook_test.go",
    "content": "package webhooks\n\nimport (\n\t\"github.com/labstack/gommon/log\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestBaseWebhook_HasEvent(t *testing.T) {\n\tbaseHook := BaseWebhook{\n\t\tLogger:   nil,\n\t\tCallback: \"http://ipsum.lorem\",\n\t\tEvents:   events.Events{events.UserUpdate},\n\t}\n\n\trequire.True(t, baseHook.HasEvent(events.UserEmailCreate))\n}\n\nfunc TestWebhooks_HasEvent_WithMultipleEvents(t *testing.T) {\n\tbaseHook := BaseWebhook{\n\t\tLogger:   nil,\n\t\tCallback: \"http://ipsum.lorem\",\n\t\tEvents:   events.Events{events.UserCreate, events.UserUpdate},\n\t}\n\n\trequire.True(t, baseHook.HasEvent(events.UserUpdate))\n}\n\nfunc TestWebhooks_HasSubEvent_WithMultipleEvents(t *testing.T) {\n\tbaseHook := BaseWebhook{\n\t\tLogger:   nil,\n\t\tCallback: \"http://ipsum.lorem\",\n\t\tEvents:   events.Events{events.UserCreate, events.UserUpdate},\n\t}\n\n\trequire.True(t, baseHook.HasEvent(events.UserEmailCreate))\n}\n\nfunc TestBaseWebhook_HasSubEvent(t *testing.T) {\n\tbaseHook := BaseWebhook{\n\t\tLogger:   nil,\n\t\tCallback: \"http://ipsum.lorem\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\trequire.True(t, baseHook.HasEvent(events.UserCreate))\n}\n\nfunc TestBaseWebhook_DoesNotHaveEvent(t *testing.T) {\n\tbaseHook := BaseWebhook{\n\t\tLogger:   nil,\n\t\tCallback: \"http://ipsum.lorem\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\trequire.False(t, baseHook.HasEvent(\"user\"))\n}\n\nfunc TestBaseWebhook_Trigger(t *testing.T) {\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\tdefer server.Close()\n\n\tbaseHook := BaseWebhook{\n\t\tLogger:   nil,\n\t\tCallback: server.URL,\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdata := JobData{\n\t\tToken: \"test-token\",\n\t\tEvent: \"user\",\n\t}\n\n\terr := baseHook.Trigger(data)\n\trequire.NoError(t, err)\n}\n\nfunc TestBaseWebhook_TriggerWithWrongUrl(t *testing.T) {\n\tbaseHook := BaseWebhook{\n\t\tLogger:   log.New(\"test\"),\n\t\tCallback: \"http://broken!\",\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdata := JobData{\n\t\tToken: \"test-token\",\n\t\tEvent: \"user\",\n\t}\n\n\terr := baseHook.Trigger(data)\n\trequire.Error(t, err)\n\trequire.Contains(t, err.Error(), \"dial tcp: lookup broken!: no such host\")\n}\n\nfunc TestBaseWebhook_TriggerWithBadStatusCode(t *testing.T) {\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t}))\n\tdefer server.Close()\n\n\tbaseHook := BaseWebhook{\n\t\tLogger:   log.New(\"test\"),\n\t\tCallback: server.URL,\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdata := JobData{\n\t\tToken: \"test-token\",\n\t\tEvent: \"user\",\n\t}\n\n\terr := baseHook.Trigger(data)\n\n\trequire.Error(t, err)\n\trequire.ErrorContains(t, err, \"request failed due to status code\")\n}\n\nfunc TestBaseWebhook_TriggerWithBadServer(t *testing.T) {\n\tserver := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttime.Sleep(600 * time.Millisecond)\n\t}))\n\tserver.Config.WriteTimeout = 500 * time.Millisecond\n\tserver.Start()\n\tdefer server.Close()\n\n\tbaseHook := BaseWebhook{\n\t\tLogger:   log.New(\"test\"),\n\t\tCallback: server.URL,\n\t\tEvents:   events.Events{events.UserCreate},\n\t}\n\n\tdata := JobData{\n\t\tToken: \"test-token\",\n\t\tEvent: \"user\",\n\t}\n\n\terr := baseHook.Trigger(data)\n\n\trequire.Error(t, err)\n\trequire.ErrorContains(t, err, \"EOF\")\n}\n\nfunc TestBaseWebhook_TriggerTimeout(t *testing.T) {\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\tdefer server.Close()\n\n\tbaseHook := BaseWebhook{\n\t\tLogger:   log.New(\"test\"),\n\t\tCallback: server.URL,\n\t\tEvents:   events.Events{events.UserCreate},\n\t\tTimeout:  20 * time.Millisecond,\n\t}\n\n\tdata := JobData{\n\t\tToken: \"test-token\",\n\t\tEvent: \"user\",\n\t}\n\n\terr := baseHook.Trigger(data)\n\n\tvar netErr net.Error\n\n\trequire.Error(t, err)\n\trequire.ErrorAs(t, err, &netErr)\n\trequire.True(t, netErr.Timeout())\n}\n"
  },
  {
    "path": "backend/webhooks/worker.go",
    "content": "package webhooks\n\nimport (\n\t\"fmt\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"time\"\n)\n\ntype Job struct {\n\tData            JobData\n\tHook            Webhook\n\tCanExpireAtTime bool\n}\n\ntype JobData struct {\n\tToken string       `json:\"token\"`\n\tEvent events.Event `json:\"event\"`\n}\n\ntype Worker struct {\n\tlogger      echo.Logger\n\thookChannel chan Job\n}\n\nfunc NewWorker(hookChannel chan Job, logger echo.Logger) Worker {\n\treturn Worker{\n\t\tlogger:      logger,\n\t\thookChannel: hookChannel,\n\t}\n}\n\nfunc (w *Worker) Run() {\n\tfor {\n\t\tjob, open := <-w.hookChannel\n\t\tif !open {\n\t\t\tbreak\n\t\t}\n\n\t\terr := w.triggerWebhook(job)\n\t\tif err != nil {\n\t\t\tw.logger.Error(fmt.Errorf(\"unable to trigger webhook: %w\", err))\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\nfunc (w *Worker) triggerWebhook(job Job) error {\n\tnow := time.Now()\n\t// only if jobs are allowed to expire\n\tif job.CanExpireAtTime {\n\t\t// check for expire date\n\t\terr := job.Hook.DisableOnExpiryDate(now)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif job.Hook.IsEnabled() {\n\t\terr := job.Hook.Trigger(job.Data)\n\t\tif err != nil {\n\t\t\t// expire after failure (if failure counter > FailureExpireRate)\n\t\t\tdisableErr := job.Hook.DisableOnFailure()\n\t\t\tif disableErr != nil {\n\t\t\t\treturn disableErr\n\t\t\t}\n\n\t\t\treturn err\n\t\t}\n\n\t\terr = job.Hook.Reset()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "backend/webhooks/worker_test.go",
    "content": "package webhooks\n\nimport (\n\t\"fmt\"\n\t\"github.com/labstack/gommon/log\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/teamhanko/hanko/backend/v2/webhooks/events\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype TestLogger struct {\n\tlog.Logger\n\tErrorLogFunc func()\n}\n\nfunc (tl *TestLogger) Error(_ ...interface{}) {\n\ttl.ErrorLogFunc()\n}\n\ntype TestWorker struct {\n\tWorker\n}\n\ntype TestHook struct {\n\tExpireFunc    func() error\n\tFailureFunc   func() error\n\tIsEnabledFunc func() bool\n\tResetFunc     func() error\n\tTriggerFunc   func() error\n}\n\nfunc (th *TestHook) DisableOnExpiryDate(_ time.Time) error {\n\treturn th.ExpireFunc()\n}\n\nfunc (th *TestHook) IsEnabled() bool {\n\treturn th.IsEnabledFunc()\n}\n\nfunc (th *TestHook) Trigger(_ JobData) error {\n\treturn th.TriggerFunc()\n}\n\nfunc (th *TestHook) DisableOnFailure() error {\n\treturn th.FailureFunc()\n}\n\nfunc (th *TestHook) Reset() error {\n\treturn th.ResetFunc()\n}\n\nfunc (th *TestHook) HasEvent(_ events.Event) bool {\n\treturn true\n}\n\nfunc TestWorker_RunWithNothing(t *testing.T) {\n\thookChannel := make(chan Job)\n\n\tworker := TestWorker{NewWorker(hookChannel, log.New(\"test\"))}\n\tclose(hookChannel)\n\tworker.Run()\n}\n\nfunc TestWorker_RunJob(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tFailureFunc: func() error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tIsEnabledFunc: func() bool {\n\t\t\t\treturn true\n\t\t\t},\n\t\t\tResetFunc: func() error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tTriggerFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t},\n\t}\n\n\thookChannel := make(chan Job, 1)\n\thookChannel <- job\n\tclose(hookChannel)\n\n\tworker := TestWorker{NewWorker(hookChannel, log.New(\"test\"))}\n\tworker.Run()\n}\n\nfunc TestWorker_RunJobWithError(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\treturn fmt.Errorf(\"forced error\")\n\t\t\t},\n\t\t},\n\t\tCanExpireAtTime: true,\n\t}\n\n\thookChannel := make(chan Job, 1)\n\thookChannel <- job\n\tclose(hookChannel)\n\n\tworker := TestWorker{NewWorker(hookChannel, &TestLogger{\n\t\tLogger:       *log.New(\"test\"),\n\t\tErrorLogFunc: func() { require.True(t, true) },\n\t})}\n\tworker.Run()\n}\n\nfunc TestWorker_TriggerWebhook(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tFailureFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tIsEnabledFunc: func() bool {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn true\n\t\t\t},\n\t\t\tResetFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tTriggerFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t},\n\t}\n\n\tworker := TestWorker{NewWorker(nil, log.New(\"test\"))}\n\terr := worker.triggerWebhook(job)\n\trequire.NoError(t, err)\n}\n\nfunc TestWorker_TriggerWebhookWithExpireError(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn fmt.Errorf(\"expired error\")\n\t\t\t},\n\t\t},\n\t\tCanExpireAtTime: true,\n\t}\n\n\tworker := TestWorker{NewWorker(nil, log.New(\"test\"))}\n\terr := worker.triggerWebhook(job)\n\trequire.ErrorContains(t, err, \"expired error\")\n}\n\nfunc TestWorker_TriggerWebhookIgnoreDisabledJob(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tIsEnabledFunc: func() bool {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn false\n\t\t\t},\n\t\t},\n\t\tCanExpireAtTime: true,\n\t}\n\n\tworker := TestWorker{NewWorker(nil, log.New(\"test\"))}\n\terr := worker.triggerWebhook(job)\n\trequire.NoError(t, err)\n}\n\nfunc TestWorker_TriggerWebhookTriggerWithError(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tIsEnabledFunc: func() bool {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn true\n\t\t\t},\n\t\t\tTriggerFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn fmt.Errorf(\"trigger error\")\n\t\t\t},\n\t\t\tFailureFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t},\n\t\tCanExpireAtTime: true,\n\t}\n\n\tworker := TestWorker{NewWorker(nil, log.New(\"test\"))}\n\terr := worker.triggerWebhook(job)\n\trequire.ErrorContains(t, err, \"trigger error\")\n}\n\nfunc TestWorker_TriggerWebhookDisableOnFailure(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tIsEnabledFunc: func() bool {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn true\n\t\t\t},\n\t\t\tTriggerFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn fmt.Errorf(\"trigger error\")\n\t\t\t},\n\t\t\tFailureFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn fmt.Errorf(\"failure error\")\n\t\t\t},\n\t\t},\n\t\tCanExpireAtTime: true,\n\t}\n\n\tworker := TestWorker{NewWorker(nil, log.New(\"test\"))}\n\terr := worker.triggerWebhook(job)\n\trequire.ErrorContains(t, err, \"failure error\")\n}\nfunc TestWorker_TriggerWebhookResetError(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tIsEnabledFunc: func() bool {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn true\n\t\t\t},\n\t\t\tTriggerFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tFailureFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tResetFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn fmt.Errorf(\"disable error\")\n\t\t\t},\n\t\t},\n\t\tCanExpireAtTime: true,\n\t}\n\n\tworker := TestWorker{NewWorker(nil, log.New(\"test\"))}\n\terr := worker.triggerWebhook(job)\n\trequire.ErrorContains(t, err, \"disable error\")\n}\nfunc TestWorker_TriggerWebhookWithDisabledTimeExpire(t *testing.T) {\n\tjob := Job{\n\t\tData: JobData{\n\t\t\tToken: \"test-token\",\n\t\t\tEvent: events.UserCreate,\n\t\t},\n\n\t\tHook: &TestHook{\n\t\t\tExpireFunc: func() error {\n\t\t\t\tassert.Fail(t, \"should not run expire func\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tIsEnabledFunc: func() bool {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn true\n\t\t\t},\n\t\t\tTriggerFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tFailureFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tResetFunc: func() error {\n\t\t\t\trequire.True(t, true)\n\t\t\t\treturn fmt.Errorf(\"disable error\")\n\t\t\t},\n\t\t},\n\t\tCanExpireAtTime: false,\n\t}\n\n\tworker := TestWorker{NewWorker(nil, log.New(\"test\"))}\n\terr := worker.triggerWebhook(job)\n\trequire.ErrorContains(t, err, \"disable error\")\n}\n"
  },
  {
    "path": "deploy/docker-compose/base.yaml",
    "content": "services:\n  hanko-migrate:\n    build: ../../backend\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    command: --config /etc/config/config.yaml migrate up\n    restart: on-failure\n    depends_on:\n      postgresd:\n        condition: service_healthy\n    networks:\n      - intranet\n  hanko:\n    depends_on:\n      hanko-migrate:\n        condition: service_completed_successfully\n    build: ../../backend\n    ports:\n      - '8000:8000' # public\n      - '8001:8001' # admin\n    restart: unless-stopped\n    command: serve --config /etc/config/config.yaml all\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    networks:\n      - intranet\n    environment:\n      - PASSWORD_ENABLED\n  postgresd:\n    image: postgres:12-alpine\n    ports:\n      - \"5432:5432\"\n    environment:\n      - POSTGRES_USER=hanko\n      - POSTGRES_PASSWORD=hanko\n      - POSTGRES_DB=hanko\n    healthcheck:\n      test: pg_isready -U hanko -d hanko\n      interval: 10s\n      timeout: 10s\n      retries: 3\n      start_period: 30s\n    networks:\n      - intranet\n  elements:\n    build: ../../frontend\n    ports:\n      - \"9500:80\"\n    networks:\n      - intranet\n  mailslurper:\n    image: marcopas/docker-mailslurper:latest\n    ports:\n      - '8080:8080' # web UI\n      - '8085:8085'\n    networks:\n      - intranet\nnetworks:\n  intranet:\n"
  },
  {
    "path": "deploy/docker-compose/config-disable-signup.yaml",
    "content": "database:\n  user: hanko\n  password: hanko\n  host: postgresd\n  port: 5432\n  dialect: postgres\nsmtp:\n    host: \"mailslurper\"\n    port: \"2500\"\npasscode:\n  email:\n    from_address: no-reply@hanko.io\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\nwebauthn:\n  relying_party:\n    origins:\n      - \"http://localhost:8888\"\nsession:\n  cookie:\n    secure: false # is needed for safari, because safari does not store secure cookies on localhost\nserver:\n  public:\n    cors:\n      allow_origins:\n        - \"http://localhost:8888\"\naccount:\n  allow_signup: false\n"
  },
  {
    "path": "deploy/docker-compose/config-rate-limiting.yaml",
    "content": "database:\n  user: hanko\n  password: hanko\n  host: postgresd\n  port: 5432\n  dialect: postgres\nsmtp:\n    host: \"mailslurper\"\n    port: \"2500\"\npasscode:\n  email:\n    from_address: no-reply@hanko.io\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\nwebauthn:\n  relying_party:\n    origins:\n    - \"http://localhost:8888\"\nsession:\n  cookie:\n    secure: false # is needed for safari, because safari does not store secure cookies on localhost\nrate_limiter:\n  enabled: true\n  store: \"redis\"\n  redis_config:\n    address: \"redis:6379\"\npassword:\n  enabled: true\nserver:\n  cors:\n    allow_origins:\n      - \"http://localhost:8888\"\n"
  },
  {
    "path": "deploy/docker-compose/config.yaml",
    "content": "database:\n  user: hanko\n  password: hanko\n  host: postgresd\n  port: 5432\n  dialect: postgres\nemail_delivery:\n  smtp:\n    host: \"mailslurper\"\n    port: \"2500\"\n  from_address: noreply@hanko.io\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\nwebauthn:\n  relying_party:\n    origins:\n      - \"http://localhost:8888\"\nsession:\n  cookie:\n    secure: false # is needed for safari, because safari does not store secure cookies on localhost\n# MFA configuration with multi-user device trust support\nmfa:\n  enabled: true\n  optional: true\n  acquire_on_login: false\n  acquire_on_registration: true\n  device_trust_policy: \"prompt\"\n  device_trust_duration: \"720h\"\n  device_trust_cookie_name: \"hanko-device-token\"\n  device_trust_max_users_per_device: 20\n  totp:\n    enabled: true\n  security_keys:\n    enabled: false\nserver:\n  public:\n    cors:\n      allow_origins:\n        - \"http://localhost:8888\"\nsecurity_notifications:\n  notifications:\n    email_create:\n      enabled: true\n    email_delete:\n      enabled: true\n    password_update:\n      enabled: true\n    passkey_create:\n      enabled: true\n    primary_email_update:\n      enabled: true\n    mfa_create:\n      enabled: true\n    mfa_delete:\n      enabled: true\n"
  },
  {
    "path": "deploy/docker-compose/quickstart-with-redis.yaml",
    "content": "services:\n  hanko-migrate:\n    build: ../../backend\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    command: --config /etc/config/config.yaml migrate up\n    restart: on-failure\n    depends_on:\n      postgresd:\n        condition: service_healthy\n    networks:\n      - intranet\n  hanko:\n    depends_on:\n      hanko-migrate:\n        condition: service_completed_successfully\n    build: ../../backend\n    ports:\n      - '8000:8000' # public\n      - '8001:8001' # admin\n    restart: unless-stopped\n    command: serve --config /etc/config/config.yaml all\n    volumes:\n      - type: bind\n        source: ./config-rate-limiting.yaml\n        target: /etc/config/config.yaml\n    networks:\n      - intranet\n    environment:\n      - PASSWORD_ENABLED\n  postgresd:\n    image: postgres:12-alpine\n    ports:\n      - \"5432:5432\"\n    environment:\n      - POSTGRES_USER=hanko\n      - POSTGRES_PASSWORD=hanko\n      - POSTGRES_DB=hanko\n    healthcheck:\n      test: pg_isready -U hanko -d hanko\n      interval: 10s\n      timeout: 10s\n      retries: 3\n      start_period: 30s\n    networks:\n      - intranet\n  elements:\n    build: ../../frontend\n    ports:\n      - \"9500:80\"\n    networks:\n      - intranet\n  quickstart:\n    build: ../../quickstart\n    ports:\n      - \"8888:8080\"\n    environment:\n      - HANKO_URL=http://localhost:8000\n      - HANKO_URL_INTERNAL=http://hanko:8000\n      - HANKO_ELEMENT_URL=http://localhost:9500/elements.js\n      - HANKO_FRONTEND_SDK_URL=http://localhost:9500/sdk.umd.js\n    networks:\n      - intranet\n  mailslurper:\n    image: marcopas/docker-mailslurper:latest\n    ports:\n      - '8080:8080' # web UI\n      - '8085:8085'\n    networks:\n      - intranet\n  redis:\n    image: redis:7-alpine\n    ports:\n      - \"6379:6379\"\n    networks:\n      - intranet\nnetworks:\n  intranet:\n"
  },
  {
    "path": "deploy/docker-compose/quickstart.debug.yaml",
    "content": "version: '3.9'\nservices:\n  hanko-migrate:\n    build: ../../backend\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    command: --config /etc/config/config.yaml migrate up\n    restart: on-failure\n    depends_on:\n      postgresd:\n        condition: service_healthy\n    networks:\n      - intranet\n  hanko:\n    depends_on:\n      hanko-migrate:\n        condition: service_completed_successfully\n    build:\n      context: ../../backend\n      dockerfile: Dockerfile.debug\n    security_opt:\n      - \"apparmor=unconfined\"\n    cap_add:\n      - SYS_PTRACE\n    ports:\n      - '8000:8000' # public\n      - '8001:8001' # admin\n      - '40000:40000' # debug\n    restart: unless-stopped\n    command: serve --config /etc/config/config.yaml all\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    networks:\n      - intranet\n    environment:\n      - PASSWORD_ENABLED\n  postgresd:\n    image: postgres:12-alpine\n    ports:\n      - \"5432:5432\"\n    environment:\n      - POSTGRES_USER=hanko\n      - POSTGRES_PASSWORD=hanko\n      - POSTGRES_DB=hanko\n    healthcheck:\n      test: pg_isready -U hanko -d hanko\n      interval: 10s\n      timeout: 10s\n      retries: 3\n      start_period: 30s\n    networks:\n      - intranet\n  elements:\n    build:\n      context: ../../frontend\n      dockerfile: Dockerfile.debug\n    ports:\n      - \"9500:80\"\n    networks:\n      - intranet\n  quickstart:\n    build: ../../quickstart\n    ports:\n      - \"8888:8080\"\n    environment:\n      - HANKO_URL=http://localhost:8000\n      - HANKO_URL_INTERNAL=http://hanko:8000\n      - HANKO_ELEMENT_URL=http://localhost:9500/elements.js\n      - HANKO_FRONTEND_SDK_URL=http://localhost:9500/sdk.modern.js\n    networks:\n      - intranet\n  mailslurper:\n    image: marcopas/docker-mailslurper:latest\n    ports:\n      - '8080:8080' # web UI\n      - '8085:8085'\n    networks:\n      - intranet\nnetworks:\n  intranet:\n"
  },
  {
    "path": "deploy/docker-compose/quickstart.e2e.yaml",
    "content": "version: '3.9'\nservices:\n  hanko-migrate:\n    build: ../../backend\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    command: --config /etc/config/config.yaml migrate up\n    restart: on-failure\n    depends_on:\n      postgresd:\n        condition: service_healthy\n    networks:\n      - intranet\n  e2e:\n    build:\n      dockerfile: Dockerfile\n      context: ../../e2e/seed\n    environment:\n      - POSTGRES_HOST=postgresd\n      - POSTGRES_PORT=5432\n      - POSTGRES_USER=hanko\n      - POSTGRES_DB=hanko\n      - PGPASSWORD=hanko\n    depends_on:\n      hanko-migrate:\n        condition: service_completed_successfully\n    networks:\n      - intranet\n  hanko:\n    depends_on:\n      hanko-migrate:\n        condition: service_completed_successfully\n    build: ../../backend\n    ports:\n      - '8000:8000' # public\n      - '8001:8001' # admin\n    restart: unless-stopped\n    command: serve --config /etc/config/config.yaml all\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    networks:\n      - intranet\n    environment:\n      - PASSWORD_ENABLED\n  postgresd:\n    image: postgres:12-alpine\n    ports:\n      - \"5432:5432\"\n    environment:\n      - POSTGRES_USER=hanko\n      - POSTGRES_PASSWORD=hanko\n      - POSTGRES_DB=hanko\n    healthcheck:\n      test: pg_isready -U hanko -d hanko\n      interval: 10s\n      timeout: 10s\n      retries: 3\n      start_period: 30s\n    networks:\n      - intranet\n  elements:\n    build: ../../frontend\n    ports:\n      - \"9500:80\"\n    networks:\n      - intranet\n  quickstart:\n    build: ../../quickstart\n    ports:\n      - \"8888:8080\"\n    environment:\n      - HANKO_URL=http://localhost:8000\n      - HANKO_URL_INTERNAL=http://hanko:8000\n      - HANKO_ELEMENT_URL=http://localhost:9500/elements.js\n      - HANKO_FRONTEND_SDK_URL=http://localhost:9500/sdk.modern.js\n    networks:\n      - intranet\n  mailslurper:\n    image: marcopas/docker-mailslurper:latest\n    ports:\n      - '8080:8080' # web UI\n      - '8085:8085'\n    networks:\n      - intranet\nnetworks:\n  intranet:\n"
  },
  {
    "path": "deploy/docker-compose/quickstart.yaml",
    "content": "version: '3.9'\nservices:\n  hanko-migrate:\n    build: ../../backend\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    command: --config /etc/config/config.yaml migrate up\n    restart: on-failure\n    depends_on:\n      postgresd:\n        condition: service_healthy\n    networks:\n      - intranet\n  hanko:\n    depends_on:\n      hanko-migrate:\n        condition: service_completed_successfully\n    build: ../../backend\n    ports:\n      - '8000:8000' # public\n      - '8001:8001' # admin\n    restart: unless-stopped\n    command: serve --config /etc/config/config.yaml all\n    volumes:\n      - type: bind\n        source: ./config.yaml\n        target: /etc/config/config.yaml\n    networks:\n      - intranet\n    environment:\n      - PASSWORD_ENABLED\n  postgresd:\n    image: postgres:12-alpine\n    ports:\n      - \"5432:5432\"\n    environment:\n      - POSTGRES_USER=hanko\n      - POSTGRES_PASSWORD=hanko\n      - POSTGRES_DB=hanko\n    healthcheck:\n      test: pg_isready -U hanko -d hanko\n      interval: 10s\n      timeout: 10s\n      retries: 3\n      start_period: 30s\n    networks:\n      - intranet\n  elements:\n    build: ../../frontend\n    ports:\n      - \"9500:80\"\n    networks:\n      - intranet\n  quickstart:\n    build: ../../quickstart\n    ports:\n      - \"8888:8080\"\n    environment:\n      - HANKO_URL=http://localhost:8000\n      - HANKO_URL_INTERNAL=http://hanko:8000\n      - HANKO_ELEMENT_URL=http://localhost:9500/elements.js\n      - HANKO_FRONTEND_SDK_URL=http://localhost:9500/sdk.modern.js\n    networks:\n      - intranet\n  mailslurper:\n    image: marcopas/docker-mailslurper:latest\n    ports:\n      - '8080:8080' # web UI\n      - '8085:8085'\n    networks:\n      - intranet\nnetworks:\n  intranet:\n"
  },
  {
    "path": "deploy/docker-compose/todo-angular.yaml",
    "content": "services:\n  todo-backend:\n    build: ../../frontend/examples/express\n    ports:\n      - \"8002:8002\"\n    environment:\n      - HANKO_API_URL=http://hanko:8000\n    networks:\n      - intranet\n  todo-frontend:\n    build: ../../frontend/examples/angular\n    ports:\n      - \"8888:8888\"\n    networks:\n      - intranet\n"
  },
  {
    "path": "deploy/docker-compose/todo-nextjs.yaml",
    "content": "services:\n  todo-backend:\n    build: ../../frontend/examples/express\n    ports:\n      - \"8002:8002\"\n    environment:\n      - HANKO_API_URL=http://hanko:8000\n    networks:\n      - intranet\n  todo-frontend:\n    build: ../../frontend/examples/nextjs\n    ports:\n      - \"8888:8888\"\n    networks:\n      - intranet\n"
  },
  {
    "path": "deploy/docker-compose/todo-react.yaml",
    "content": "services:\n  todo-backend:\n    build: ../../frontend/examples/express\n    ports:\n      - \"8002:8002\"\n    environment:\n      - HANKO_API_URL=http://hanko:8000\n    networks:\n      - intranet\n  todo-frontend:\n    build: ../../frontend/examples/react\n    ports:\n      - \"8888:8888\"\n    networks:\n      - intranet\n"
  },
  {
    "path": "deploy/docker-compose/todo-svelte.yaml",
    "content": "services:\n  todo-backend:\n    build: ../../frontend/examples/express\n    ports:\n      - \"8002:8002\"\n    environment:\n      - HANKO_API_URL=http://hanko:8000\n    networks:\n      - intranet\n  todo-frontend:\n    build: ../../frontend/examples/svelte\n    ports:\n      - \"8888:8888\"\n    networks:\n      - intranet\n"
  },
  {
    "path": "deploy/docker-compose/todo-vue.yaml",
    "content": "services:\n  todo-backend:\n    build: ../../frontend/examples/express\n    ports:\n      - \"8002:8002\"\n    environment:\n      - HANKO_API_URL=http://hanko:8000\n    networks:\n      - intranet\n  todo-frontend:\n    build: ../../frontend/examples/vue\n    ports:\n      - \"8888:8888\"\n    networks:\n      - intranet\n"
  },
  {
    "path": "deploy/k8s/README.md",
    "content": "# k8s deploy\nThis folder is used internaly by hanko to test hanko in a kubernetes setup using [skaffold](https://skaffold.dev/) and [kustomize](https://github.com/kubernetes-sigs/kustomize)\nrunning in a [kind cluster](https://kind.sigs.k8s.io/) that got set up by [gentle](https://github.com/like-a-bause/gentle).\n\nWhile this is a \"hanko tailored\" setup you can use these yamls as a base for your own kubernetes deployment.\n"
  },
  {
    "path": "deploy/k8s/base/elements/deployment.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: hanko-elements\n  namespace: hanko-tenant\n  labels:\n    app: hanko-elements\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: hanko-elements\n  template:\n    metadata:\n      labels:\n        app: hanko-elements\n    spec:\n      containers:\n        - name: hanko-elements\n          image: ghcr.io/teamhanko/hanko/elements:main\n          imagePullPolicy: IfNotPresent\n          env:\n            - name: HANKO_URL\n              value: https://hanko.quickstart.test\n            - name: HANKO_URL_INTERNAL\n              value: http://hanko-public.svc\n            - name: HANKO_ELEMENT_URL\n              value: http://localhost:9500/elements.js\n            - name: HANKO_FRONTEND_SDK_URL\n              value: http://localhost:9500/sdk.modern.js\n          ports:\n            - name: http-public\n              containerPort: 80\n              protocol: TCP\n"
  },
  {
    "path": "deploy/k8s/base/elements/ingress.yaml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: hanko-elements\n  namespace: hanko-tenant\n  annotations:\n    kubernetes.io/ingress.class: \"nginx\"\n    cert-manager.io/cluster-issuer: \"letsencrypt-prod\"\n    nginx.ingress.kubernetes.io/ssl-redirect: \"true\"\n    nginx.ingress.kubernetes.io/force-ssl-redirect: \"true\"\n  labels:\n    fqdn: elements.quickstart.test\nspec:\n  tls:\n    - hosts:\n        - $(ELEMENTS_FQDN)\n      secretName: elements-tls\n  rules:\n    - host: $(ELEMENTS_FQDN)\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: elements\n                port:\n                  name: http\n"
  },
  {
    "path": "deploy/k8s/base/elements/kustomization.yaml",
    "content": "resources:\n  - deployment.yaml\n  - service.yaml\n  - ingress.yaml\nvars:\n  - fieldref:\n      fieldpath: metadata.labels.fqdn\n    name: ELEMENTS_FQDN\n    objref:\n      apiVersion: networking.k8s.io/v1\n      kind: Ingress\n      name: hanko-elements\n"
  },
  {
    "path": "deploy/k8s/base/elements/service.yaml",
    "content": "---\napiVersion: v1\nkind: Service\nmetadata:\n  name: elements\n  namespace: hanko-tenant\nspec:\n  selector:\n    app: hanko-elements\n  ports:\n    - port: 80\n      targetPort: http-public\n      protocol: TCP\n      name: http\n"
  },
  {
    "path": "deploy/k8s/base/mailhog/deployment.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: mailhog\n  namespace: hanko\nspec:\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        app: mailhog\n    spec:\n      containers:\n        - name: mailhog\n          image:  mailhog/mailhog:latest\n          ports:\n            - containerPort: 8025\n              name: mailhog-ui\n            - containerPort: 1025\n              name: smtp\n  selector:\n    matchLabels:\n      app: mailhog\n"
  },
  {
    "path": "deploy/k8s/base/mailhog/ingress.yaml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  namespace: hanko-tenant\n  name: mailhog\n  annotations:\n    kubernetes.io/ingress.class: \"nginx\"\n    cert-manager.io/cluster-issuer: \"letsencrypt-prod\"\n    nginx.ingress.kubernetes.io/ssl-redirect: \"true\"\n    nginx.ingress.kubernetes.io/force-ssl-redirect: \"true\"\n  labels:\n    fqdn: mail.quickstart.test\nspec:\n  tls:\n    - hosts:\n        - $(MAILHOG_FQDN)\n      secretName: mail-tls\n  rules:\n    - host: $(MAILHOG_FQDN)\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: mailhog\n                port:\n                  name: mailhog-ui\n"
  },
  {
    "path": "deploy/k8s/base/mailhog/kustomization.yaml",
    "content": "resources:\n  - deployment.yaml\n  - service.yaml\n  - ingress.yaml\nvars:\n  - fieldref:\n      fieldpath: metadata.labels.fqdn\n    name: MAILHOG_FQDN\n    objref:\n      apiVersion: networking.k8s.io/v1\n      kind: Ingress\n      name: mailhog\n"
  },
  {
    "path": "deploy/k8s/base/mailhog/service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  name: mailhog\n  namespace: hanko\nspec:\n  ports:\n    - port: 8080\n      name: mailhog-ui\n      targetPort: mailhog-ui\n    - port: 8085\n      name: service-port\n      targetPort: service-port\n    - port: 2500\n      name: smtp\n      targetPort: smtp\n  selector:\n    app: mailhog\n"
  },
  {
    "path": "deploy/k8s/base/postgres/deployment.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: postgres\n  namespace: hanko\nspec:\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        app: postgres\n    spec:\n      containers:\n        - name: postgres\n          image: postgres:12-alpine\n          ports:\n            - containerPort: 5432\n              name: postgres-hanko\n          env:\n            - name: POSTGRES_DB\n              value: hanko\n            - name: POSTGRES_USER\n              value: hanko\n            - name: POSTGRES_PASSWORD\n              value: hanko\n          volumeMounts:\n            - mountPath: /docker-entrypoint-initdb.d\n              name: initdb\n            - mountPath: /var/lib/postgresql/data\n              name: postgres-pv-claim\n      volumes:\n        - name: initdb\n          configMap:\n            name: initdb\n        - name: postgres-pv-claim\n          persistentVolumeClaim:\n            claimName: postgres-pv-claim\n  selector:\n    matchLabels:\n      app: postgres\n"
  },
  {
    "path": "deploy/k8s/base/postgres/initdbscript.sh",
    "content": "#!/bin/bash\nset -e\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\nCREATE DATABASE hanko;\nGRANT ALL PRIVILEGES ON DATABASE hanko TO $POSTGRES_USER;\nEOSQL"
  },
  {
    "path": "deploy/k8s/base/postgres/kustomization.yaml",
    "content": "resources:\n  - deployment.yaml\n  - service.yaml\n  - persistent-volume.yaml\nconfigMapGenerator:\n  - name: initdb\n    files:\n      - initdbscript.sh\n"
  },
  {
    "path": "deploy/k8s/base/postgres/persistent-volume.yaml",
    "content": "# Persistence Volume definition removed because it somehow made problems with the postgres container.\n# Volume now gets created dynamically (based on the claim).\n---\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  labels:\n    app: postgres\n  name: postgres-pv-claim\n  namespace: hanko\nspec:\n  storageClassName: standard\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 100M\n"
  },
  {
    "path": "deploy/k8s/base/postgres/service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  name: postgres\n  namespace: hanko\nspec:\n  ports:\n    - port: 5432\n  selector:\n    app: postgres"
  },
  {
    "path": "deploy/k8s/base/quickstart/deployment.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: hanko-quickstart\n  namespace: hanko-tenant\n  labels:\n    app: hanko-quickstart\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: hanko-quickstart\n  template:\n    metadata:\n      labels:\n        app: hanko-quickstart\n    spec:\n      containers:\n        - name: hanko-quickstart\n          image: ghcr.io/teamhanko/hanko/quickstart:main\n          imagePullPolicy: IfNotPresent\n          env:\n            - name: HANKO_URL\n              value: https://hanko.quickstart.test\n            - name: HANKO_URL_INTERNAL\n              value: http://hanko-public.svc\n            - name: HANKO_ELEMENT_URL\n              value: https://elements.quickstart.test/elements.js\n            - name: HANKO_FRONTEND_SDK_URL\n              value: https://elements.quickstart.test/sdk.modern.js\n          ports:\n            - name: http-public\n              containerPort: 8080\n              protocol: TCP\n"
  },
  {
    "path": "deploy/k8s/base/quickstart/ingress.yaml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  namespace: hanko-tenant\n  name: hanko-quickstart\n  annotations:\n    kubernetes.io/ingress.class: \"nginx\"\n    cert-manager.io/cluster-issuer: \"letsencrypt-prod\"\n    nginx.ingress.kubernetes.io/ssl-redirect: \"true\"\n    nginx.ingress.kubernetes.io/force-ssl-redirect: \"true\"\n  labels:\n    fqdn: app.quickstart.test\nspec:\n  tls:\n    - hosts:\n        - $(QUICKSTART_FQDN)\n      secretName: quickstart-tls\n  rules:\n    - host: $(QUICKSTART_FQDN)\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: quickstart\n                port:\n                  name: http\n"
  },
  {
    "path": "deploy/k8s/base/quickstart/kustomization.yaml",
    "content": "resources:\n  - deployment.yaml\n  - service.yaml\n  - ingress.yaml\nvars:\n  - fieldref:\n      fieldpath: metadata.labels.fqdn\n    name: QUICKSTART_FQDN\n    objref:\n      apiVersion: networking.k8s.io/v1\n      kind: Ingress\n      name: hanko-quickstart\n"
  },
  {
    "path": "deploy/k8s/base/quickstart/service.yaml",
    "content": "---\napiVersion: v1\nkind: Service\nmetadata:\n  name: quickstart\n  namespace: hanko-tenant\nspec:\n  selector:\n    app: hanko-quickstart\n  ports:\n    - port: 80\n      targetPort: http-public\n      protocol: TCP\n      name: http\n"
  },
  {
    "path": "deploy/k8s/overlays/quickstart/kustomization.yaml",
    "content": "namespace: hanko-quickstart\nresources:\n  - ../../base/postgres\n  - ../../base/hanko\n  - ../../base/elements\n  - ../../base/quickstart\n  - ../../base/mailhog\n"
  },
  {
    "path": "deploy/k8s/overlays/thirdparty-x-domain/README.md",
    "content": "# Adding OIDC Clients\nTo successfully test this you need to add OIDC Clients as Secrets:\n\nCreate a github.env and a google.env of the form:\n```\nclient_id=your-id\nclient_secret=your-secret\n```\n\nRun\n> skaffold run -p thirdparty-x-domain\n\nto build and deploy to local cluster.\n\nThe quickstart app should then be running on **https://app.domain-app.grocery**\n"
  },
  {
    "path": "deploy/k8s/overlays/thirdparty-x-domain/config.yaml",
    "content": "database:\n  user: hanko\n  password: hanko\n  host: postgres\n  port: 5432\n  dialect: postgres\nsmtp:\n    host: \"mailhog\"\n    port: \"2500\"\npasscode:\n  email:\n    from_address: no-reply@hanko.io\nsecrets:\n  keys:\n    - abcedfghijklmnopqrstuvwxyz\nservice:\n  name: Hanko Authentication Service\nsession:\n  enable_auth_token_header: true\nserver:\n  public:\n    cors:\n      enabled: true\n      allow_credentials: true\n      allow_origins:\n        - 'https://app.domain-app.grocery'\nwebauthn:\n  relying_party:\n    id: \"app.domain-app.grocery\"\n    origins:\n      - \"https://app.domain-app.grocery\"\nthird_party:\n  error_redirect_url: https://app.domain-app.grocery\n  allowed_redirect_urls:\n    - https://app.domain-app.grocery**\n  redirect_url: https://hanko.domain-hanko.grocery/thirdparty/callback\n  providers:\n    google:\n      enabled: true\n    github:\n      enabled: true\n    apple:\n      enabled: true\n"
  },
  {
    "path": "deploy/k8s/overlays/thirdparty-x-domain/env-patch.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: hanko-quickstart\n  namespace: hanko-tenant\nspec:\n  template:\n    spec:\n      containers:\n        - name: hanko-quickstart\n          env:\n            - name: HANKO_URL\n              value: https://hanko.domain-hanko.grocery\n            - name: HANKO_URL_INTERNAL\n              value: http://hanko-public\n            - name: HANKO_ELEMENT_URL\n              value: https://elements.domain-app.grocery/elements.js\n            - name: HANKO_FRONTEND_SDK_URL\n              value: https://elements.domain-app.grocery/sdk.modern.js\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: hanko\n  namespace: hanko-tenant\nspec:\n  template:\n    spec:\n      containers:\n        - name: hanko\n          env:\n            - name: THIRD_PARTY_PROVIDERS_GOOGLE_CLIENT_ID\n              valueFrom:\n                secretKeyRef:\n                  key: client_id\n                  name: google\n            - name: THIRD_PARTY_PROVIDERS_GOOGLE_SECRET\n              valueFrom:\n                secretKeyRef:\n                  key: client_secret\n                  name: google\n            - name: THIRD_PARTY_PROVIDERS_GITHUB_CLIENT_ID\n              valueFrom:\n                secretKeyRef:\n                  key: client_id\n                  name: github\n            - name: THIRD_PARTY_PROVIDERS_GITHUB_SECRET\n              valueFrom:\n                secretKeyRef:\n                  key: client_secret\n                  name: github\n            - name: THIRD_PARTY_PROVIDERS_APPLE_CLIENT_ID\n              valueFrom:\n                secretKeyRef:\n                  key: client_id\n                  name: apple\n            - name: THIRD_PARTY_PROVIDERS_APPLE_SECRET\n              valueFrom:\n                secretKeyRef:\n                  key: client_secret\n                  name: apple\n            - name: THIRD_PARTY_PROVIDERS_FACEBOOK_CLIENT_ID \n              valueFrom:\n                secretKeyRef:\n                  key: client_id\n                  name: facebook \n            - name: THIRD_PARTY_PROVIDERS_FACEBOOK_SECRET \n              valueFrom:\n                secretKeyRef:\n                  key: client_secret\n                  name: facebook \n      initContainers:\n        - name: hanko-migrate\n          env:\n            - name: THIRD_PARTY_PROVIDERS_GOOGLE_CLIENT_ID\n              valueFrom:\n                secretKeyRef:\n                  key: client_id\n                  name: google\n            - name: THIRD_PARTY_PROVIDERS_GOOGLE_SECRET\n              valueFrom:\n                secretKeyRef:\n                  key: client_secret\n                  name: google\n            - name: THIRD_PARTY_PROVIDERS_GITHUB_CLIENT_ID\n              valueFrom:\n                secretKeyRef:\n                  key: client_id\n                  name: github\n            - name: THIRD_PARTY_PROVIDERS_GITHUB_SECRET\n              valueFrom:\n                secretKeyRef:\n                  key: client_secret\n                  name: github\n            - name: THIRD_PARTY_PROVIDERS_APPLE_CLIENT_ID\n              valueFrom:\n                secretKeyRef:\n                  key: client_id\n                  name: apple\n            - name: THIRD_PARTY_PROVIDERS_APPLE_SECRET\n              valueFrom:\n                secretKeyRef:\n                  key: client_secret\n                  name: apple"
  },
  {
    "path": "deploy/k8s/overlays/thirdparty-x-domain/ingress-patch.yaml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: hanko\n  namespace: hanko-tenant\n  labels:\n    fqdn: hanko.domain-hanko.grocery\n---\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: hanko-elements\n  namespace: hanko-tenant\n  labels:\n    fqdn: elements.domain-app.grocery\n---\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: hanko-quickstart\n  namespace: hanko-tenant\n  labels:\n    fqdn: app.domain-app.grocery\n---\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: mailhog\n  namespace: hanko-tenant\n  labels:\n    fqdn: mail.domain-app.grocery\n"
  },
  {
    "path": "deploy/k8s/overlays/thirdparty-x-domain/kustomization.yaml",
    "content": "namespace: hanko-thirdparty-x-domain\nresources:\n  - ../../base/postgres\n  - ../../base/hanko\n  - ../../base/elements\n  - ../../base/quickstart\n  - ../../base/mailhog\npatchesStrategicMerge:\n  - ingress-patch.yaml\n  - env-patch.yaml\nconfigMapGenerator:\n  - files:\n      - config.yaml\n    name: hanko\n    behavior: replace\nsecretGenerator:\n  - name: github\n    envs:\n      - github.env\n  - name: google\n    envs:\n      - google.env\n  - name: apple\n    envs:\n      - apple.env\n  - name: facebook\n    envs:\n      - facebook.env\n"
  },
  {
    "path": "e2e/.eslintignore",
    "content": "node_modules\n"
  },
  {
    "path": "e2e/.eslintrc.cjs",
    "content": "module.exports = {\n  env: {\n    browser: true,\n    es2021: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"plugin:playwright/playwright-test\",\n    \"plugin:prettier/recommended\",\n  ],\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    ecmaVersion: \"latest\",\n    sourceType: \"module\",\n  },\n  plugins: [\"@typescript-eslint\"],\n  rules: {\n    \"@typescript-eslint/no-non-null-assertion\": \"off\"\n  },\n};\n"
  },
  {
    "path": "e2e/.nvmrc",
    "content": "v18.6.0\n"
  },
  {
    "path": "e2e/README.md",
    "content": "# End 2 End Tests\n\nThis directory contains E2E tests for the Hanko project using [Playwright](https://playwright.dev/).\n\n# Contents\n\n- [Prerequisites](#prerequisites)\n  - [Required software](#required-software)\n  - [Required services](#required-services)\n- [Run the tests](#run-the-tests)\n  - [Set up services using Docker Compose](#set-up-services-using-docker-compose)\n  - [Install project dependencies](#install-project-dependencies)\n  - [Execute the tests](#execute-the-tests)\n\n# Prerequisites\n\n## Required software\n\nTo run the tests you need to have the following software installed:\n\n- [Node](https://nodejs.org) v. 18.6.0+/ npm\n- [Docker](https://www.docker.com/) / Docker Compose\n\n\n## Required services\n\nFurthermore, you need running instances of:\n\n- the Hanko [backend](../backend)\n- a running frontend application (e.g. our [quickstart](../quickstart)) using the web component provided by\n  [hanko-elements](../frontend/elements)\n- [Mailslurper](https://github.com/mailslurper/mailslurper) as an SMTP server (used to test passcodes through mail\n  retrieval via its [API](https://github.com/mailslurper/mailslurper/wiki/API-Guide))\n\nThe tests distinguish between password-based and passwordless scenarios. Each of these requires the proper\n[backend](../backend) configuration, i.e. it must be configured to run with either passwords enabled or disabled.\n\n# Run the tests\n\n## Set up services using Docker Compose\n\nTo get everything up and running, you can use the existing Docker Compose quickstart in\nthe [`deploy/docker-compose`](../deploy/docker-compose) directory. From the root project directory, execute:\n\n**Passwords disabled**:\n\n```shell\n# compose v1\ndocker-compose -f deploy/docker-compose/quickstart.yaml -p \"hanko-quickstart-nopw\" up --build\n\n# compose v2\ndocker compose -f deploy/docker-compose/quickstart.yaml -p \"hanko-quickstart-nopw\" up --build\n\n```\n\n**Passwords enabled**:\n\n```shell\n# compose v1\nPASSWORD_ENABLED=true docker-compose -f deploy/docker-compose/quickstart.yaml -p \"hanko-quickstart-pw\" up --build\n\n# compose v2\nPASSWORD_ENABLED=true docker compose -f deploy/docker-compose/quickstart.yaml -p \"hanko-quickstart-pw\" up --build\n\n```\n\nor add the following to the `deploy/docker-compose/config.yaml`\n\n```yaml\npassword:\n    enabled: true\n```\n\nand then run\n\n```shell\n# compose v1\ndocker-compose -f deploy/docker-compose/quickstart.yaml -p \"hanko-quickstart-pw\" up --build\n\n# compose v2\ndocker compose -f deploy/docker-compose/quickstart.yaml -p \"hanko-quickstart-pw\" up --build\n\n```\n\n## Install project dependencies\n\nOnce the services are up and running, install dependencies from inside the `e2e` directory:\n\n`npm install`\n\n`npx playwright install chromium`\n\n## Execute the tests\n\nThen execute the tests using:\n\n**Passwords disabled**:\n\n`npm run test:nopw`\n\n**Passwords enabled**:\n\n`npm run test:pw`\n\n> **Note**: If VSCode is your IDE of choice, you can use\n> the [Playwright extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) to\n> run a test or a group of tests with a single click.\n\nFor more information on how to customize npm scripts to run tests using the Playwright CLI please view\nthe official [documentation](https://playwright.dev/docs/test-cli).\n\n\n"
  },
  {
    "path": "e2e/fixtures/Pages.ts",
    "content": "import { expect, test as base } from \"@playwright/test\";\nimport { LoginEmail } from \"../pages/LoginEmail.js\";\nimport { RegisterConfirm } from \"../pages/RegisterConfirm.js\";\nimport { LoginPasscode } from \"../pages/LoginPasscode.js\";\nimport { RegisterAuthenticator } from \"../pages/RegisterAuthenticator.js\";\nimport { SecuredContent } from \"../pages/SecuredContent.js\";\nimport { LoginPassword } from \"../pages/LoginPassword.js\";\nimport { RegisterPassword } from \"../pages/RegisterPassword.js\";\nimport { LoginEmailNoSignup } from \"../pages/LoginEmailNoSignUp.js\";\nimport { NoAccountFound } from \"../pages/NoAccountFound.js\";\nimport { MailSlurper } from \"../helper/MailSlurper.js\";\nimport * as Matchers from \"../helper/Matchers.js\";\nimport { Error } from \"../pages/Error.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\nimport Setup from \"../helper/Setup.js\";\n\nexport type Pages = {\n  errorPage: Error;\n  loginEmailPage: LoginEmail;\n  loginEmailNoSignupPage: LoginEmailNoSignup;\n  noAccountFoundPage: NoAccountFound,\n  registerConfirmPage: RegisterConfirm;\n  loginPasscodePage: LoginPasscode;\n  loginPasswordPage: LoginPassword;\n  registerPasswordPage: RegisterPassword;\n  registerAuthenticatorPage: RegisterAuthenticator;\n  securedContentPage: SecuredContent;\n};\n\nexport type AuthenticatorOptions = {\n  protocol?: string;\n  transport?: string;\n  hasResidentKey?: boolean;\n  hasUserVerification?: boolean;\n  isUserVerified?: boolean;\n};\n\nexport type WebAuthnOptions = {\n  enabled: boolean;\n  authenticator?: AuthenticatorOptions;\n};\n\nexport type TestOptions = {\n  webauthn: WebAuthnOptions;\n};\n\nexport const test = base.extend<TestOptions & Pages>({\n  webauthn: [{ enabled: false }, { option: true }],\n\n  errorPage: async ({ page }, use) => {\n    await use(new Error(page));\n  },\n\n  loginEmailPage: async ({ baseURL, page, webauthn }, use) => {\n    await Setup.webauthn(page, webauthn);\n\n    await Promise.all([\n      page.waitForResponse(Endpoints.API.WELL_KNOWN_CONFIG),\n      page.goto(baseURL!),\n    ]);\n\n    const loginEmailPage: LoginEmail = new LoginEmail(page);\n    await use(loginEmailPage);\n  },\n\n  loginEmailNoSignupPage: async ({ baseURL, page }, use) => { \n    await Promise.all([\n      page.waitForResponse(Endpoints.API.WELL_KNOWN_CONFIG),\n      page.goto(baseURL!),\n    ]);\n    const loginEmailPageNoSignup: LoginEmailNoSignup = new LoginEmailNoSignup(page);\n    await use(loginEmailPageNoSignup);\n  },\n\n  noAccountFoundPage: async ({ page }, use) => {\n    await use(new NoAccountFound(page));\n  },\n\n  registerConfirmPage: async ({ page }, use) => {\n    await use(new RegisterConfirm(page));\n  },\n\n  loginPasscodePage: async ({ page }, use) => {\n    const mail = new MailSlurper();\n    const loginPasscode = new LoginPasscode(page, mail);\n    await use(loginPasscode);\n  },\n\n  loginPasswordPage: async ({ page }, use) => {\n    await use(new LoginPassword(page));\n  },\n\n  registerAuthenticatorPage: async ({ page }, use) => {\n    await use(new RegisterAuthenticator(page));\n  },\n\n  registerPasswordPage: async ({ page }, use) => {\n    await use(new RegisterPassword(page));\n  },\n\n  securedContentPage: async ({ page }, use) => {\n    await use(new SecuredContent(page));\n  },\n});\n\nexpect.extend({\n  ...Matchers,\n});\n\nexport { expect };\n"
  },
  {
    "path": "e2e/global.d.ts",
    "content": "export {};\n\ndeclare global {\n  namespace PlaywrightTest {\n    interface Matchers<R> {\n      toHaveCookie(name?: string): R;\n      toHaveLocalStorageEntry(origin?: string, name?: string): R;\n      toHaveLocalStorageEntryForUserWithCredential(\n        userId: string,\n        credentialId: string,\n        origin?: string,\n        name?: string\n      ): R;\n      toHaveLocalStorageEntryForUserWithPasscode(\n        userId: string,\n        passcodeId: string,\n        origin?: string,\n        name?: string\n      ): R;\n    }\n  }\n}\n"
  },
  {
    "path": "e2e/helper/Accounts.ts",
    "content": "const Accounts = {\n    test: {\n        email: \"test@example.com\"\n    }\n};\n\nexport default Accounts;\n"
  },
  {
    "path": "e2e/helper/Endpoints.ts",
    "content": "const Endpoints = {\n  API: {\n    ME: \"**/me\",\n    USER: \"**/user\",\n    USERS: \"**/users\",\n    USERS_PARAM: \"**/users/*\",\n    PASSWORD: \"**/password\",\n    PASSWORD_LOGIN: \"**/password/login\",\n    WEBAUTHN_LOGIN_INITIALIZE: \"**/webauthn/login/initialize\",\n    WEBAUTHN_LOGIN_FINALIZE: \"**/webauthn/login/finalize\",\n    WEBAUTHN_REGISTRATION_INITIALIZE: \"**/webauthn/registration/initialize\",\n    WEBAUTHN_REGISTRATION_FINALIZE: \"**/webauthn/registration/finalize\",\n    PASSCODE_LOGIN_INITIALIZE: \"**/passcode/login/initialize\",\n    PASSCODE_LOGIN_FINALIZE: \"**/passcode/login/finalize\",\n    WELL_KNOWN_CONFIG: \"**/.well-known/config\",\n  },\n  APP: {\n    LOGOUT: \"**/logout\",\n    SECURED_CONTENT: \"**/secured\",\n  },\n};\n\nexport default Endpoints;\n"
  },
  {
    "path": "e2e/helper/MailSlurper.ts",
    "content": "import fetch from \"node-fetch\";\n\nexport interface Mails {\n  mailItems: Mail[];\n}\n\nexport interface Mail {\n  id: string;\n  dateSent: string;\n  fromAddress: string;\n  toAddresses: string[];\n  subject: string;\n  xmailer: string;\n  body: string;\n  contentType: string;\n  boundary: string;\n  attachments: Attachment[];\n}\n\nexport interface Attachment {\n  id: string;\n  mailId: string;\n  headers: Headers;\n  contents: string;\n}\n\nexport interface Headers {\n  contentType: string;\n  mimeVersion: string;\n  contentTransferEncoding: string;\n  contentDisposition: string;\n  fileName: string;\n  body: string;\n}\n\nexport class MailSlurper {\n  api: string;\n\n  constructor(protocol = \"http\", host = \"localhost\", port = 8085) {\n    this.api = `${protocol}://${host}:${port}`;\n  }\n\n  async getMails(recipient: string): Promise<Mails> {\n    const url = recipient\n      ? `${this.api}/mail?to=${recipient}`\n      : `${this.api}/mail`;\n    const response = await fetch(url);\n    return (await response.json()) as Mails;\n  }\n\n  async getPasscodeFromMail(mail: Mail): Promise<string> {\n    const passcode = mail.body.match(\"[0-9]{6}\");\n\n    if (passcode) {\n      return passcode[0];\n    } else {\n      throw new Error(\n        `could not extract passcode from mail with id'${mail.id}'`\n      );\n    }\n  }\n\n  async getPasscodeFromMostRecentMail(recipient: string): Promise<string> {\n    const mails: Mails = await this.getMails(recipient);\n\n    if (mails.mailItems.length === 0) {\n      throw new Error(`no mails found for '${recipient}'`);\n    }\n\n    return this.getPasscodeFromMail(mails.mailItems[0]);\n  }\n}\n"
  },
  {
    "path": "e2e/helper/Matchers.ts",
    "content": "import { BasePage } from \"../pages/BasePage.js\";\n\nexport async function toHaveCookie(received: BasePage, name = \"hanko\") {\n  if (typeof received.hasCookie !== \"function\") {\n    return {\n      message: () => `matcher only applicable to type BasePage`,\n      pass: false,\n    };\n  }\n\n  const pass = await received.hasCookie(name);\n  if (pass) {\n    return {\n      message: () => `cookie with '${name}' present`,\n      pass: true,\n    };\n  } else {\n    return {\n      message: () => `no cookie with name '${name}' present`,\n      pass: false,\n    };\n  }\n}\n\nexport async function toHaveLocalStorageEntry(\n  received: BasePage,\n  origin = \"http://localhost:8888\",\n  name = \"hanko\"\n) {\n  if (typeof received.getLocalStorageValue !== \"function\") {\n    return {\n      message: () => `matcher only applicable to type BasePage`,\n      pass: false,\n    };\n  }\n\n  const localStorageEntry = await received.getLocalStorageValue(origin, name);\n\n  if (localStorageEntry) {\n    return {\n      message: () =>\n        `local storage entry for origin '${origin}' with key '${name}' present`,\n      pass: true,\n    };\n  } else {\n    return {\n      message: () =>\n        `local storage entry for origin '${origin}' with key '${name}' not present`,\n      pass: false,\n    };\n  }\n}\n\nexport async function toHaveLocalStorageEntryForUserWithCredential(\n  received: BasePage,\n  userId: string,\n  credentialId: string,\n  origin = \"http://localhost:8888\",\n  name = \"hanko\"\n) {\n  if (typeof received.getDecodedLocalStorageValue !== \"function\") {\n    return {\n      message: () => `matcher only applicable to type BasePage`,\n      pass: false,\n    };\n  }\n\n  const { users } = await received.getDecodedLocalStorageValue(origin, name);\n\n  if (!users) {\n    return {\n      message: () =>\n        `credential '${credentialId}' for user '${userId}' present`,\n      pass: false,\n    };\n  }\n\n  const userState = users[userId];\n  const pass = userState.webauthn.credentials.includes(credentialId);\n\n  if (pass) {\n    return {\n      message: () =>\n        `credential '${credentialId}' for user '${userId}' present`,\n      pass: true,\n    };\n  } else {\n    return {\n      message: () =>\n        `credential '${credentialId}' for user '${userId}' not present`,\n      pass: false,\n    };\n  }\n}\n\nexport async function toHaveLocalStorageEntryForUserWithPasscode(\n  received: BasePage,\n  userId: string,\n  passcodeId: string,\n  origin = \"http://localhost:8888\",\n  name = \"hanko\"\n) {\n  if (typeof received.getDecodedLocalStorageValue !== \"function\") {\n    return {\n      message: () => `matcher only applicable to type BasePage`,\n      pass: false,\n    };\n  }\n\n  const { users } = await received.getDecodedLocalStorageValue(origin, name);\n\n  if (!users) {\n    return {\n      message: () => `local storage value not present`,\n      pass: false,\n    };\n  }\n\n  const userState = users[userId];\n\n  if (!userState) {\n    return {\n      message: () => `user state for user ${userId} not present`,\n      pass: false,\n    };\n  }\n\n  const pass = userState.passcode.id === passcodeId;\n\n  if (pass) {\n    return {\n      message: () => `passcode '${passcodeId}' for user '${userId}' present`,\n      pass: true,\n    };\n  } else {\n    return {\n      message: () =>\n        `passcode '${passcodeId}' for user '${userId}' not present`,\n      pass: false,\n    };\n  }\n}\n"
  },
  {
    "path": "e2e/helper/Setup.ts",
    "content": "import type { Page } from \"@playwright/test\";\nimport type { WebAuthnOptions } from \"../fixtures/Pages.js\";\n\nasync function setUpWebAuthn(page: Page, options: WebAuthnOptions) {\n  const defaultAuthenticatorOptions = {\n    protocol: \"ctap2\",\n    transport: \"internal\",\n    hasResidentKey: true,\n    hasUserVerification: true,\n    isUserVerified: true,\n  };\n\n  if (options.enabled) {\n    const client = await page.context().newCDPSession(page);\n    await client.send(\"WebAuthn.enable\");\n    await client.send(\"WebAuthn.addVirtualAuthenticator\", {\n      // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n      // @ts-ignore\n      options: {\n        ...defaultAuthenticatorOptions,\n        ...options.authenticator,\n      },\n    });\n  }\n}\n\nconst Setup = {\n  webauthn: setUpWebAuthn,\n};\n\nexport default Setup;\n"
  },
  {
    "path": "e2e/package.json",
    "content": "{\n  \"name\": \"@teamhanko/hanko-e2e\",\n  \"version\": \"1.0.0\",\n  \"description\": \"End to end tests fo the hanko project\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test:nopw\": \"npx playwright test passwordless common\",\n    \"test:pw\": \"npx playwright test passwords common\",\n    \"test:nosignup\": \"npx playwright test nosignup\",\n    \"test:report\": \"npx playwright show-report\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/teamhanko/hanko.git\"\n  },\n  \"keywords\": [\n    \"test\",\n    \"e2e\"\n  ],\n  \"author\": \"Hanko GmbH <developers@hanko.io>\",\n  \"license\": \"AGPL-3.0-or-later\",\n  \"bugs\": {\n    \"url\": \"https://github.com/teamhanko/hanko/issues\"\n  },\n  \"homepage\": \"https://github.com/teamhanko/hanko#readme\",\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^7.2.0\",\n    \"@playwright/test\": \"^1.25.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.28.0\",\n    \"@typescript-eslint/parser\": \"^5.28.0\",\n    \"eslint\": \"^8.17.0\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-plugin-playwright\": \"^0.9.0\",\n    \"eslint-plugin-prettier\": \"^4.0.0\",\n    \"node-fetch\": \"^3.2.6\",\n    \"prettier\": \"^2.7.1\",\n    \"typescript\": \"^4.7.3\"\n  }\n}\n"
  },
  {
    "path": "e2e/pages/BasePage.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\n\nexport abstract class BasePage {\n  readonly page: Page;\n  readonly errorMessage: Locator;\n\n  constructor(page: Page) {\n    this.page = page;\n    this.errorMessage = page.locator(\"id=errorMessage\");\n  }\n\n  async hasCookie(name = \"hanko\") {\n    const cookies = await this.page.context().cookies();\n    const found = cookies.find((cookie) => cookie.name === name);\n    return !!found;\n  }\n\n  async getLocalStorageValue(origin = \"http://localhost:8888\", key = \"hanko\") {\n    const storageState = await this.page.context().storageState();\n    const store = storageState.origins.find((o) => o.origin === origin);\n    const entry = store?.localStorage.find((entry) => entry.name == key);\n    if (entry) {\n      return entry.value;\n    } else {\n      return null;\n    }\n  }\n\n  async getDecodedLocalStorageValue(\n    origin = \"http://localhost:8888\",\n    key = \"hanko\"\n  ) {\n    const value = await this.getLocalStorageValue(origin, key);\n    if (value) {\n      const buf = new Buffer(value, \"base64\").toString();\n      return JSON.parse(decodeURIComponent(decodeURI(buf)));\n    } else {\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "e2e/pages/Error.ts",
    "content": "import { BasePage } from \"./BasePage.js\";\nimport type { Locator, Page } from \"@playwright/test\";\nimport Endpoints from \"../helper/Endpoints.js\";\n\nexport class Error extends BasePage {\n  readonly headline: Locator;\n  readonly continueButton: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.headline = page.locator(\"h1\", {\n      hasText: \"An error has occurred\",\n    });\n    this.continueButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Continue\",\n    });\n  }\n\n  async continue() {\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.WELL_KNOWN_CONFIG),\n      this.continueButton.click(),\n    ]);\n  }\n}\n"
  },
  {
    "path": "e2e/pages/LoginEmail.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\n\nexport class LoginEmail extends BasePage {\n  readonly emailInput: Locator;\n  readonly continueButton: Locator;\n  readonly signInPasskeyButton: Locator;\n  readonly headline: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.emailInput = page.locator(\"input[name=email]\");\n    this.continueButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Continue\",\n    });\n    this.signInPasskeyButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Sign in with a passkey\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"Sign in or sign up\" });\n  }\n\n  async continueUsingEmail(email: string) {\n    await this.emailInput.fill(email);\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.USER),\n      this.continueButton.click(),\n    ]);\n  }\n\n  async signInWithPasskey() {\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.WEBAUTHN_LOGIN_INITIALIZE),\n      this.page.waitForResponse(Endpoints.API.WEBAUTHN_LOGIN_FINALIZE),\n      this.signInPasskeyButton.click(),\n    ]);\n  }\n}\n"
  },
  {
    "path": "e2e/pages/LoginEmailNoSignUp.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\n\nexport class LoginEmailNoSignup extends BasePage {\n  readonly emailInput: Locator;\n  readonly continueButton: Locator;\n  readonly signInPasskeyButton: Locator;\n  readonly headline: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.emailInput = page.locator(\"input[name=email]\");\n    this.continueButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Continue\",\n    });\n    this.signInPasskeyButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Sign in with a passkey\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"Sign in\" });\n  }\n\n  async continueUsingEmail(email: string) {\n    await this.emailInput.fill(email);\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.USER),\n      this.continueButton.click(),\n    ]);\n  }\n\n  async signInWithPasskey() {\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.WEBAUTHN_LOGIN_INITIALIZE),\n      this.page.waitForResponse(Endpoints.API.WEBAUTHN_LOGIN_FINALIZE),\n      this.signInPasskeyButton.click(),\n    ]);\n  }\n}\n"
  },
  {
    "path": "e2e/pages/LoginPasscode.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { MailSlurper } from \"../helper/MailSlurper.js\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\n\nexport class LoginPasscode extends BasePage {\n  readonly continueButton: Locator;\n  readonly sendNewCodeLink: Locator;\n  readonly headline: Locator;\n  readonly mailSlurperClient: MailSlurper;\n  readonly passcodeInputs: Locator;\n\n  constructor(page: Page, mailSlurperClient: MailSlurper) {\n    super(page);\n    this.passcodeInputs = page.locator(\"input[name*='passcode']\");\n    this.mailSlurperClient = mailSlurperClient;\n    this.continueButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Continue\",\n    });\n    this.sendNewCodeLink = page.locator(\"button\", {\n      hasText: \"Send new code\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"Enter passcode\" });\n  }\n\n  async signInWithPasscodeFor(email: string, passcode?: string) {\n    // Waiting is discouraged, but we need to give Mailslurper some time to\n    // process inbound messages.\n    await this.page.waitForTimeout(1000);\n\n    if (!passcode) {\n      passcode = await this.mailSlurperClient.getPasscodeFromMostRecentMail(\n        email\n      );\n    }\n    const digits = passcode.split(\"\");\n\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.PASSCODE_LOGIN_FINALIZE),\n      this.submitPasscode(digits),\n    ]);\n  }\n\n  async submitPasscode(digits: string[]) {\n    for (let i = 0; i < 6; ++i)\n      await this.page.locator(`input[name=passcode${i}]`).fill(digits[i]);\n  }\n\n  async sendNewCode() {\n    // Introduce some artificial timeout before sending so that Mailslurper does\n    // apply the same 'dateSent' values to inbound mails and ordering does not\n    // get messed up when retrieving mails via the API.\n    await this.page.waitForTimeout(1000);\n\n    const [response] = await Promise.all([\n      this.page.waitForResponse(Endpoints.API.PASSCODE_LOGIN_INITIALIZE),\n      this.sendNewCodeLink.click(),\n    ]);\n\n    return await response.json();\n  }\n}\n"
  },
  {
    "path": "e2e/pages/LoginPassword.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\n\nexport class LoginPassword extends BasePage {\n  readonly passwordInput: Locator;\n  readonly signInButton: Locator;\n  readonly backLink: Locator;\n  readonly forgotPasswordLink: Locator;\n  readonly headline: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.passwordInput = page.locator(\"input[name=password]\");\n    this.signInButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Sign in\",\n    });\n    this.backLink = page.locator(\"button\", { hasText: \"Back\" });\n    this.forgotPasswordLink = page.locator(\"button\", {\n      hasText: \"Forgot your password?\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"Enter password\" });\n  }\n\n  async submitPassword(password: string) {\n    await this.passwordInput.fill(password);\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.PASSWORD_LOGIN),\n      this.signInButton.click(),\n    ]);\n  }\n\n  async back() {\n    await this.backLink.click();\n  }\n\n  async recovery() {\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.PASSCODE_LOGIN_INITIALIZE),\n      this.forgotPasswordLink.click(),\n    ]);\n  }\n}\n"
  },
  {
    "path": "e2e/pages/NoAccountFound.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport { expect } from \"../fixtures/Pages.js\";\n\nexport class NoAccountFound extends BasePage {\n  readonly backLink: Locator;\n  readonly signUpButton: Locator;\n  readonly headline: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.backLink = page.locator(\"button\", { hasText: \"Back\" });\n    this.signUpButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Sign up\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"No account found\" });\n  }\n\n  async assertSignupButtonNotVisible() {\n    await expect(this.signUpButton).not.toBeVisible();\n  }\n\n  async assertNoAccountFoundText(email: string) {\n    const text = this.page.locator(\"p\", {hasText: `No account exists for \"${email}\".`});\n    await expect(text).toBeVisible();\n  }\n\n  async back() {\n    await this.backLink.click();\n  }\n}\n"
  },
  {
    "path": "e2e/pages/RegisterAuthenticator.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\nimport { expect } from \"../fixtures/Pages.js\";\n\nexport class RegisterAuthenticator extends BasePage {\n  readonly setUpPasskeyButton: Locator;\n  readonly skipLink: Locator;\n  readonly headline: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.setUpPasskeyButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Create a passkey\",\n    });\n    this.skipLink = page.locator(\"button\", {\n      hasText: \"Skip\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"Create a passkey\" });\n  }\n\n  async registerPasskey() {\n    const [initResponse, finalResponse] = await Promise.all([\n      this.page.waitForResponse(Endpoints.API.WEBAUTHN_REGISTRATION_INITIALIZE),\n      this.page.waitForResponse(Endpoints.API.WEBAUTHN_REGISTRATION_FINALIZE),\n      this.setUpPasskeyButton.click(),\n    ]);\n\n    const initResponseJson = await initResponse.json();\n    const finalResponseJson = await finalResponse.json();\n    const { user_id: userId, credential_id: credentialId } = finalResponseJson;\n\n    await expect(\n      this,\n      \"The credential is encoded in the local storage user state\"\n    ).toHaveLocalStorageEntryForUserWithCredential(userId, credentialId);\n\n    return [initResponseJson, finalResponseJson];\n  }\n\n  async skip() {\n    await this.skipLink.click();\n  }\n}\n"
  },
  {
    "path": "e2e/pages/RegisterConfirm.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\nimport { expect } from \"../fixtures/Pages.js\";\n\nexport class RegisterConfirm extends BasePage {\n  readonly backLink: Locator;\n  readonly signUpButton: Locator;\n  readonly headline: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.backLink = page.locator(\"button\", { hasText: \"Back\" });\n    this.signUpButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Sign up\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"Create account?\" });\n  }\n\n  async confirmRegistration() {\n    const [usersResponse, passcodeInitResponse] = await Promise.all([\n      this.page.waitForResponse(Endpoints.API.USERS),\n      this.page.waitForResponse(Endpoints.API.PASSCODE_LOGIN_INITIALIZE),\n      this.signUpButton.click(),\n    ]);\n\n    const usersResponseJson = await usersResponse.json();\n    const passcodeInitResponseJson = await passcodeInitResponse.json();\n\n    await expect(\n      this,\n      \"The passcode is encoded in the local storage user state\"\n    ).toHaveLocalStorageEntryForUserWithPasscode(\n      usersResponseJson.id,\n      passcodeInitResponseJson.id\n    );\n\n    return [usersResponseJson, passcodeInitResponseJson];\n  }\n\n  async back() {\n    await this.backLink.click();\n  }\n}\n"
  },
  {
    "path": "e2e/pages/RegisterPassword.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\n\nexport class RegisterPassword extends BasePage {\n  readonly passwordInput: Locator;\n  readonly continueButton: Locator;\n  readonly headline: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.passwordInput = page.locator(\"input[name=password]\");\n    this.continueButton = page.locator(\"button[type=submit]\", {\n      hasText: \"Continue\",\n    });\n    this.headline = page.locator(\"h1\", { hasText: \"Set new password\" });\n  }\n\n  async submitPassword(password: string) {\n    await this.passwordInput.fill(password);\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.API.PASSWORD),\n      this.continueButton.click(),\n    ]);\n  }\n}\n"
  },
  {
    "path": "e2e/pages/SecuredContent.ts",
    "content": "import type { Locator, Page } from \"@playwright/test\";\nimport { BasePage } from \"./BasePage.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\nimport { expect } from \"../fixtures/Pages.js\";\n\nexport class SecuredContent extends BasePage {\n  readonly logoutLink: Locator;\n\n  constructor(page: Page) {\n    super(page);\n    this.logoutLink = page.locator(\"a\", { hasText: \"Logout\" });\n  }\n\n  async logout() {\n    await Promise.all([\n      this.page.waitForResponse(Endpoints.APP.LOGOUT),\n      this.logoutLink.click(),\n    ]);\n\n    await expect(\n      this,\n      \"Logging out should clear the cookie\"\n    ).not.toHaveCookie();\n  }\n}\n"
  },
  {
    "path": "e2e/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from \"@playwright/test\";\nimport { devices } from \"@playwright/test\";\nimport type { TestOptions } from \"./fixtures/Pages.js\";\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nconst config: PlaywrightTestConfig<TestOptions> = {\n  /* Maximum time one test can run for. */\n  timeout: 30 * 1000,\n  expect: {\n    /**\n     * Maximum time expect() should wait for the condition to be met.\n     * For example in `await expect(locator).toHaveText();`\n     */\n    timeout: 5000,\n  },\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !!process.env.CI,\n  /* Retry on CI only */\n  retries: process.env.CI ? 2 : 0,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.CI ? 1 : undefined,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: process.env.CI ? \"github\" : \"html\",\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */\n    actionTimeout: 0,\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    baseURL: \"http://localhost:8888\",\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: process.env.CI ? \"on-first-retry\" : \"on\",\n  },\n  /* Directory that will be recursively scanned for test files. Defaults to the directory of the configuration file. */\n  testDir: \"./tests\",\n  /* Configure projects */\n  projects: [\n    {\n      name: \"chromium\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n      },\n    },\n  ],\n};\n\nexport default config;\n"
  },
  {
    "path": "e2e/seed/Dockerfile",
    "content": "FROM postgres:12-alpine\n\n\nARG POSTGRES_HOST\nARG POSTGRES_PORT\nARG POSTGRES_USER\nARG POSTGRES_DB\nARG PGPASSWORD\n\nCOPY seed.sql ./seed.sql\nCOPY init.sh ./init.sh\n\nRUN chmod +x ./init.sh\n\nCMD ./init.sh\n"
  },
  {
    "path": "e2e/seed/init.sh",
    "content": "#!/bin/bash\n\npsql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -a -w -f seed.sql\n"
  },
  {
    "path": "e2e/seed/seed.sql",
    "content": "INSERT INTO users\n(id, created_at, updated_at)\nVALUES\n('357461f1-458a-42c8-abf3-05eabfc36ffd', current_timestamp, current_timestamp);\n\nINSERT INTO emails\n(id, user_id, address, verified, created_at, updated_at)\nVALUES\n('47c082da-b70a-4ccc-bc5f-1481b3499273', '357461f1-458a-42c8-abf3-05eabfc36ffd', 'test@example.com', true, current_timestamp, current_timestamp);\n\nINSERT INTO primary_emails\n(id, email_id, user_id, created_at, updated_at)\nVALUES\n('8de035cd-3d21-415c-8844-644fe40d7d74', '47c082da-b70a-4ccc-bc5f-1481b3499273', '357461f1-458a-42c8-abf3-05eabfc36ffd', current_timestamp, current_timestamp);\n"
  },
  {
    "path": "e2e/tests/common.spec.ts",
    "content": "import { test, expect } from \"../fixtures/Pages.js\";\nimport { LoginEmail } from \"../pages/LoginEmail.js\";\nimport type { Mails } from \"../helper/MailSlurper.js\";\nimport Endpoints from \"../helper/Endpoints.js\";\nimport { faker } from \"@faker-js/faker\";\n\ntest.describe(\"@common\", () => {\n  test.describe(\"@nowebauthn\", () => {\n    test(\"Error page on faulty config\", async ({\n      baseURL,\n      page,\n      errorPage,\n    }) => {\n      await test.step(\"Given the API returns an error response once when the public config is requested\", async () => {\n        await page.route(\n          Endpoints.API.WELL_KNOWN_CONFIG,\n          async (route) => {\n            const response = await page.request.fetch(route.request());\n            await route.fulfill({\n              response,\n              status: 500,\n            });\n          },\n          { times: 1 }\n        );\n      });\n\n      await test.step(\"When I visit the base URL\", async () => {\n        await page.goto(baseURL!);\n      });\n\n      await test.step(\"The error page should be shown\", async () => {\n        await expect(errorPage.headline).toBeVisible();\n        await expect(errorPage.errorMessage).toBeVisible();\n      });\n\n      await test.step(\"And when clicking the continue button\", async () => {\n        await errorPage.continue();\n      });\n\n      await test.step(\"The LoginEmail page should be shown\", async () => {\n        const loginEmailPage = new LoginEmail(page);\n        await expect(loginEmailPage.headline).toBeVisible();\n      });\n    });\n\n    test(\"Expired passcode\", async ({\n      page,\n      loginEmailPage,\n      registerConfirmPage,\n      loginPasscodePage,\n    }) => {\n      const email = faker.internet.email();\n\n      await test.step(\"Given the API creates a passcode that expires immediately\", async () => {\n        await page.route(\n          Endpoints.API.PASSCODE_LOGIN_INITIALIZE,\n          async (route) => {\n            const response = await page.request.fetch(route.request());\n            const body = await response.json();\n            await route.fulfill({\n              response,\n              body: JSON.stringify({ ...body, ttl: 0 }),\n            });\n          },\n          { times: 1 }\n        );\n      });\n\n      await test.step(\"When I submit an email address\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The RegisterConfirm page should be shown\", async () => {\n        await expect(registerConfirmPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I confirm the registration\", async () => {\n        await registerConfirmPage.confirmRegistration();\n      });\n\n      await test.step(\"The passcode login page should be shown\", async () => {\n        await expect(loginPasscodePage.headline).toBeVisible();\n      });\n\n      await test.step(\"And a passcode expiry message should be shown\", async () => {\n        await expect(loginPasscodePage.errorMessage).toHaveText(/expired/);\n      });\n\n      await test.step(\"And input elements should be disabled\", async () => {\n        await expect(loginPasscodePage.continueButton).toBeDisabled();\n        for (let i = 0; i < 6; ++i)\n          await expect(\n            loginPasscodePage.page.locator(`input[name=passcode${i}]`)\n          ).toBeDisabled();\n      });\n\n      await test.step(\"And when I request a new passcode\", async () => {\n        await loginPasscodePage.sendNewCode();\n      });\n\n      await test.step(\"The error message should be hidden\", async () => {\n        await expect(loginPasscodePage.errorMessage).toBeHidden();\n      });\n\n      await test.step(\"And input elements should be enabled\", async () => {\n        await expect(loginPasscodePage.continueButton).toBeEnabled();\n        for (let i = 0; i < 6; ++i)\n          await expect(\n            loginPasscodePage.page.locator(`input[name=passcode${i}]`)\n          ).toBeEnabled();\n      });\n    });\n\n    test(\"Requesting a new passcode but logging in with an old passcode fails\", async ({\n      loginEmailPage,\n      loginPasscodePage,\n      registerConfirmPage,\n    }) => {\n      const email = faker.internet.email();\n\n      await test.step(\"When I visit the baseURL, the LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I submit an email address\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The RegisterConfirm page should be shown\", async () => {\n        await expect(registerConfirmPage.headline).toBeVisible();\n      });\n\n      let userId: string;\n      let passcodeId: string;\n\n      await test.step(\"And when I confirm the registration\", async () => {\n        [{ id: userId }, { id: passcodeId }] =\n          await registerConfirmPage.confirmRegistration();\n      });\n\n      await test.step(\"The LoginPasscode page should be shown\", async () => {\n        await expect(loginPasscodePage.headline).toBeVisible();\n      });\n\n      await test.step(\"And I should receive a passcode email\", async () => {\n        const mails = await loginPasscodePage.mailSlurperClient.getMails(email);\n        await expect(mails.mailItems).toHaveLength(1);\n      });\n\n      let newPasscodeId: string;\n\n      await test.step(\"And when I request a new passcode\", async () => {\n        ({ id: newPasscodeId } = await loginPasscodePage.sendNewCode());\n      });\n\n      await test.step(\"The API responds with a new passcode\", async () => {\n        await expect(newPasscodeId).not.toBe(passcodeId);\n      });\n\n      await test.step(\"And the old passcode in the local storage user state is replaced\", async () => {\n        await expect(\n          loginPasscodePage\n        ).toHaveLocalStorageEntryForUserWithPasscode(userId, newPasscodeId);\n      });\n\n      let mails: Mails;\n\n      await test.step(\"And I receive a new passcode email\", async () => {\n        mails = await loginPasscodePage.mailSlurperClient.getMails(email);\n        await expect(mails.mailItems).toHaveLength(2);\n      });\n\n      await test.step(\"And when I submit the old passcode\", async () => {\n        const oldPasscode =\n          await loginPasscodePage.mailSlurperClient.getPasscodeFromMail(\n            mails.mailItems[1]\n          );\n\n        await loginPasscodePage.signInWithPasscodeFor(email, oldPasscode);\n      });\n\n      await test.step(\"An error should be shown\", async () => {\n        await expect(loginPasscodePage.errorMessage).toBeVisible();\n      });\n\n      await test.step(\"And no cookie should be created\", async () => {\n        await expect(loginPasscodePage).not.toHaveCookie();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "e2e/tests/nosignup.spec.ts",
    "content": "import { test, expect } from \"../fixtures/Pages.js\";\nimport { faker } from \"@faker-js/faker\";\nimport Accounts from \"../helper/Accounts.js\";\n\ntest.describe(\"@nosignup\", () => {\n    test(\"Login with account that does not exist\", async ({\n        loginEmailNoSignupPage,\n        noAccountFoundPage\n    }) => {\n        const email = faker.internet.email();\n\n        await test.step(\"When I visit the baseURL, the LoginEmailNoSignup page should be shown\", async () => {\n            await expect(loginEmailNoSignupPage.headline).toBeVisible();\n            await expect(loginEmailNoSignupPage.signInPasskeyButton).toBeVisible();\n        });\n\n        await test.step(\"And when I submit an email address\", async () => {\n            await loginEmailNoSignupPage.continueUsingEmail(email);\n        });\n\n        await test.step(\"No account should be found\", async () => {\n            await noAccountFoundPage.assertNoAccountFoundText(email);\n        });\n\n        await test.step(\"Signup button should not be visible\", async() => {\n            await noAccountFoundPage.assertSignupButtonNotVisible();\n        });\n\n        await test.step(\"Navigating back should take me back to LoginEmailNoSignup page\", async () => {\n            await noAccountFoundPage.back();\n            await expect(loginEmailNoSignupPage.headline).toBeVisible();\n            await expect(loginEmailNoSignupPage.signInPasskeyButton).toBeVisible();\n        });\n    });\n\n    test(\"Login with existing account\", async ({\n        loginEmailNoSignupPage,\n        loginPasscodePage\n    }) => {\n        const email = Accounts.test.email;\n\n        await test.step(\"When I visit the baseURL, the LoginEmailNoSignup page should be shown\", async () => {\n            await expect(loginEmailNoSignupPage.headline).toBeVisible();\n            await expect(loginEmailNoSignupPage.signInPasskeyButton).toBeVisible();\n        });\n\n        await test.step(\"And when I submit an email that already exists\", async () => {\n            await loginEmailNoSignupPage.continueUsingEmail(email);\n        });\n\n        await test.step(\"Login passocde page should be displayed\", async () => {\n            await expect(loginPasscodePage.headline).toBeVisible();\n        });\n    });\n});\n"
  },
  {
    "path": "e2e/tests/passwordless.spec.ts",
    "content": "import { test, expect } from \"../fixtures/Pages.js\";\nimport { faker } from \"@faker-js/faker\";\nimport Endpoints from \"../helper/Endpoints.js\";\nimport Accounts from \"../helper/Accounts.js\";\n\ntest.describe(\"@nopw\", () => {\n  test.beforeEach(async ({}) => {\n    faker.seed();\n  });\n\n  test.describe(\"@webauthn\", () => {\n    const transports = [\"internal\", \"usb\", \"nfc\", \"ble\"];\n    for (const transport of transports) {\n      test.use({\n        webauthn: {\n          enabled: true,\n          authenticator: {\n            transport: transport,\n          },\n        },\n      });\n\n      test(`Register, Create a passkey, logout, login with passkey with authenticator transport ${transport}`, async ({\n        loginEmailPage,\n        registerConfirmPage,\n        loginPasscodePage,\n        registerAuthenticatorPage,\n        securedContentPage,\n      }) => {\n        const email = faker.internet.email();\n\n        await test.step(\"When I visit the baseURL, the LoginEmail page should be shown\", async () => {\n          await expect(loginEmailPage.headline).toBeVisible();\n          await expect(loginEmailPage.signInPasskeyButton).toBeVisible();\n        });\n\n        await test.step(\"And when I submit an email address\", async () => {\n          await loginEmailPage.continueUsingEmail(email);\n        });\n\n        await test.step(\"The RegisterConfirm page should be shown\", async () => {\n          await expect(registerConfirmPage.headline).toBeVisible();\n        });\n\n        await test.step(\"And when I confirm the registration\", async () => {\n          await registerConfirmPage.confirmRegistration();\n        });\n\n        await test.step(\"The LoginPasscode page should be shown\", async () => {\n          await expect(loginPasscodePage.headline).toBeVisible();\n        });\n\n        await test.step(\"And I should receive a passcode email\", async () => {\n          const mails = await loginPasscodePage.mailSlurperClient.getMails(\n            email\n          );\n          await expect(mails.mailItems).toHaveLength(1);\n        });\n\n        await test.step(\"And when I submit the passcode\", async () => {\n          await loginPasscodePage.signInWithPasscodeFor(email);\n        });\n\n        await test.step(\"The RegisterAuthenticator page should be shown\", async () => {\n          await expect(registerAuthenticatorPage.headline).toBeVisible();\n        });\n\n        await test.step(\"And a cookie should be set\", async () => {\n          await expect(registerAuthenticatorPage).toHaveCookie();\n        });\n\n        await test.step(\"And when I register a WebAuthn credential\", async () => {\n          await registerAuthenticatorPage.registerPasskey();\n        });\n\n        await test.step(\"The SecuredContent page should be shown\", async () => {\n          await registerAuthenticatorPage.page.waitForURL(\n            Endpoints.APP.SECURED_CONTENT\n          );\n\n          await expect(securedContentPage.logoutLink).toBeVisible();\n        });\n\n        await test.step(\"And when I log out\", async () => {\n          await securedContentPage.logout();\n        });\n\n        await test.step(\"The LoginEmail page should be shown\", async () => {\n          await expect(loginEmailPage.headline).toBeVisible();\n        });\n\n        await test.step(\"And when I login with the previously registered WebAuthn credential\", async () => {\n          await loginEmailPage.signInWithPasskey();\n        });\n\n        await test.step(\"The SecuredContent page should be shown\", async () => {\n          await expect(securedContentPage.logoutLink).toBeVisible();\n        });\n      });\n    }\n  });\n\n  test.describe(\"@nowebauthn\", () => {\n    test(\"Register, login with passcode\", async ({\n      loginEmailPage,\n      loginPasscodePage,\n      registerConfirmPage,\n      registerAuthenticatorPage,\n      securedContentPage,\n    }) => {\n      const email = faker.internet.email();\n\n      await test.step(\"When I visit the baseURL, the LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n        await expect(loginEmailPage.signInPasskeyButton).toBeVisible();\n      });\n\n      await test.step(\"And when I submit an email address\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The RegisterConfirm page should be shown\", async () => {\n        await expect(registerConfirmPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I confirm the registration\", async () => {\n        await registerConfirmPage.confirmRegistration();\n      });\n\n      await test.step(\"The LoginPasscode page should be shown\", async () => {\n        await expect(loginPasscodePage.headline).toBeVisible();\n      });\n\n      await test.step(\"And I should receive a passcode email\", async () => {\n        const mails = await loginPasscodePage.mailSlurperClient.getMails(email);\n        await expect(mails.mailItems).toHaveLength(1);\n      });\n\n      await test.step(\"And when I submit the passcode\", async () => {\n        await loginPasscodePage.signInWithPasscodeFor(email);\n      });\n\n      await test.step(\"The RegisterAuthenticator page should be shown\", async () => {\n        await expect(registerAuthenticatorPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And a cookie should be set\", async () => {\n        await expect(registerAuthenticatorPage).toHaveCookie();\n      });\n\n      await test.step(\"And when I skip WebAuthn credential registration\", async () => {\n        await registerAuthenticatorPage.skip();\n      });\n\n      await test.step(\"The SecuredContent page should be shown\", async () => {\n        await expect(securedContentPage.logoutLink).toBeVisible();\n      });\n\n      await test.step(\"And a cookie should have been set\", async () => {\n        await expect(securedContentPage).toHaveCookie();\n      });\n    });\n\n    test(\"Logging in with existing user will prompt for passcode\", async ({\n      loginEmailPage,\n      loginPasscodePage\n    }) => {\n      const email = Accounts.test.email;\n\n      await test.step(\"When I visit the baseURL, the LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I submit an email address\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The LoginPasscode page should be shown\", async () => {\n        await expect(loginPasscodePage.headline).toBeVisible();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "e2e/tests/passwords.spec.ts",
    "content": "import { test, expect } from \"../fixtures/Pages.js\";\nimport { faker } from \"@faker-js/faker\";\nimport Endpoints from \"../helper/Endpoints.js\";\nimport Accounts from \"../helper/Accounts.js\";\n\ntest.describe(\"@pw\", () => {\n  test.beforeEach(async ({}) => {\n    faker.seed();\n  });\n\n  test.describe(\"@webauthn\", () => {\n    test.use({\n      webauthn: {\n        enabled: true,\n      },\n    });\n\n    test(\"Register, set up password, skip passkey, logout, login with password, register passkey\", async ({\n      loginEmailPage,\n      registerConfirmPage,\n      loginPasscodePage,\n      loginPasswordPage,\n      registerPasswordPage,\n      registerAuthenticatorPage,\n      securedContentPage,\n    }) => {\n      const email = faker.internet.email();\n      const password = faker.internet.password();\n\n      await test.step(\"When I visit the baseURL, the LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n        await expect(loginEmailPage.signInPasskeyButton).toBeVisible();\n      });\n\n      await test.step(\"And when I submit an email address\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The RegisterConfirm page should be shown\", async () => {\n        await expect(registerConfirmPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I confirm the registration\", async () => {\n        await registerConfirmPage.confirmRegistration();\n      });\n\n      await test.step(\"The LoginPasscode page should be shown\", async () => {\n        await expect(loginPasscodePage.headline).toBeVisible();\n      });\n\n      await test.step(\"And I should receive a passcode email\", async () => {\n        const mails = await loginPasscodePage.mailSlurperClient.getMails(email);\n        await expect(mails.mailItems).toHaveLength(1);\n      });\n\n      await test.step(\"And when I submit the passcode\", async () => {\n        await loginPasscodePage.signInWithPasscodeFor(email);\n      });\n\n      await test.step(\"The RegisterPassword page should be shown\", async () => {\n        await expect(registerPasswordPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And a cookie should be set\", async () => {\n        await expect(registerPasswordPage).toHaveCookie();\n      });\n\n      await test.step(\"And when I set up a password\", async () => {\n        await registerPasswordPage.submitPassword(password);\n      });\n\n      await test.step(\"The RegisterAuthenticator page should be shown\", async () => {\n        await expect(registerAuthenticatorPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I skip WebAuthn credential registration\", async () => {\n        await registerAuthenticatorPage.skip();\n      });\n\n      await test.step(\"The SecuredContent page should be shown\", async () => {\n        await registerAuthenticatorPage.page.waitForURL(\n          Endpoints.APP.SECURED_CONTENT\n        );\n\n        await expect(securedContentPage.logoutLink).toBeVisible();\n      });\n\n      await test.step(\"And when I log out\", async () => {\n        await securedContentPage.logout();\n      });\n\n      await test.step(\"The LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I log in with the previously set password\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n        await expect(loginPasswordPage.headline).toBeVisible();\n        await loginPasswordPage.submitPassword(password);\n      });\n\n      await test.step(\"The RegisterAuthenticator page should be shown again\", async () => {\n        await expect(registerAuthenticatorPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And a cookie should be set\", async () => {\n        await expect(registerAuthenticatorPage).toHaveCookie();\n      });\n\n      await test.step(\"And when I register a WebAuthn credential\", async () => {\n        await registerAuthenticatorPage.registerPasskey();\n      });\n\n      await test.step(\"The SecuredContent page should be shown\", async () => {\n        await registerAuthenticatorPage.page.waitForURL(\n          Endpoints.APP.SECURED_CONTENT\n        );\n\n        await expect(securedContentPage.logoutLink).toBeVisible();\n      });\n    });\n  });\n\n  test.describe(\"@nowebauthn\", () => {\n    test(\"Password recovery\", async ({\n      loginEmailPage,\n      registerConfirmPage,\n      loginPasscodePage,\n      loginPasswordPage,\n      registerPasswordPage,\n      registerAuthenticatorPage,\n      securedContentPage,\n    }) => {\n      const email = faker.internet.email();\n      const password = faker.internet.password();\n\n      await test.step(\"When I visit the baseURL, the LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n        await expect(loginEmailPage.signInPasskeyButton).toBeVisible();\n      });\n\n      await test.step(\"And when I submit an email address\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The RegisterConfirm page should be shown\", async () => {\n        await expect(registerConfirmPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I confirm the registration\", async () => {\n        await registerConfirmPage.confirmRegistration();\n      });\n\n      await test.step(\"The LoginPasscode page should be shown\", async () => {\n        await expect(loginPasscodePage.headline).toBeVisible();\n      });\n\n      await test.step(\"And I should receive a passcode email\", async () => {\n        const mails = await loginPasscodePage.mailSlurperClient.getMails(email);\n        await expect(mails.mailItems).toHaveLength(1);\n      });\n\n      await test.step(\"And when I submit the passcode\", async () => {\n        await loginPasscodePage.signInWithPasscodeFor(email);\n      });\n\n      await test.step(\"The RegisterPassword page should be shown\", async () => {\n        await expect(registerPasswordPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And a cookie should be set\", async () => {\n        await expect(registerPasswordPage).toHaveCookie();\n      });\n\n      await test.step(\"And when I set up a password\", async () => {\n        await registerPasswordPage.submitPassword(password);\n      });\n\n      await test.step(\"The RegisterAuthenticator page should be shown\", async () => {\n        await expect(registerAuthenticatorPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I skip WebAuthn credential registration\", async () => {\n        await registerAuthenticatorPage.skip();\n      });\n\n      await test.step(\"The SecuredContent page should be shown\", async () => {\n        await registerPasswordPage.page.waitForURL(\n          Endpoints.APP.SECURED_CONTENT\n        );\n\n        await expect(securedContentPage.logoutLink).toBeVisible();\n      });\n\n      await test.step(\"And when I log out\", async () => {\n        await securedContentPage.logout();\n      });\n\n      await test.step(\"The LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I submit my email address again\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The LoginPassword page should be shown\", async () => {\n        await expect(loginPasswordPage.headline).toBeVisible();\n        await expect(loginPasswordPage.forgotPasswordLink).toBeVisible();\n      });\n\n      await test.step(\"And when I trigger password recovery\", async () => {\n        await loginPasswordPage.recovery();\n      });\n\n      await test.step(\"The LoginPasscode page should be shown\", async () => {\n        await expect(loginPasscodePage.headline).toBeVisible();\n      });\n\n      await test.step(\"And I should receive another passcode email\", async () => {\n        const mails = await loginPasscodePage.mailSlurperClient.getMails(email);\n        await expect(mails.mailItems).toHaveLength(2);\n      });\n\n      await test.step(\"And when I submit the passcode\", async () => {\n        await loginPasscodePage.signInWithPasscodeFor(email);\n      });\n\n      await test.step(\"The RegisterPassword page should be shown\", async () => {\n        await expect(registerPasswordPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And a cookie should have been set\", async () => {\n        await expect(registerPasswordPage).toHaveCookie();\n      });\n\n      const newPassword = faker.internet.password();\n      await test.step(\"And when I set a new password\", async () => {\n        await registerPasswordPage.submitPassword(newPassword);\n      });\n\n      await test.step(\"The RegisterAuthenticator page should be shown\", async () => {\n        await expect(registerAuthenticatorPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I skip WebAuthn credential registration\", async () => {\n        await registerAuthenticatorPage.skip();\n      });\n\n      await test.step(\"The SecuredContent page should be shown\", async () => {\n        await expect(securedContentPage.logoutLink).toBeVisible();\n      });\n\n      await test.step(\"And when I log out and log in with the old password\", async () => {\n        await securedContentPage.logout();\n        await loginEmailPage.continueUsingEmail(email);\n        await loginPasswordPage.submitPassword(password);\n      });\n\n      await test.step(\"An invalid credentials error should be shown\", async () => {\n        await expect(loginPasswordPage.errorMessage).toHaveText(\n          \"Wrong email or password.\"\n        );\n      });\n\n      await test.step(\"And when I log in with the new password\", async () => {\n        await loginPasswordPage.submitPassword(newPassword);\n      });\n\n      await test.step(\"The RegisterAuthenticator page should be shown\", async () => {\n        await expect(registerAuthenticatorPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I skip WebAuthn credential registration\", async () => {\n        await registerAuthenticatorPage.skip();\n      });\n\n      await test.step(\"The SecuredContent page should be shown\", async () => {\n        await expect(securedContentPage.logoutLink).toBeVisible();\n      });\n\n      await test.step(\"And a cookie should have been set\", async () => {\n        await expect(securedContentPage).toHaveCookie();\n      });\n    });\n\n    test(\"Logging in with existing user will prompt for password\", async ({\n      loginEmailPage,\n      loginPasswordPage\n    }) => {\n      const email = Accounts.test.email;\n\n      await test.step(\"When I visit the baseURL, the LoginEmail page should be shown\", async () => {\n        await expect(loginEmailPage.headline).toBeVisible();\n      });\n\n      await test.step(\"And when I submit an email address\", async () => {\n        await loginEmailPage.continueUsingEmail(email);\n      });\n\n      await test.step(\"The LoginPassword page should be shown\", async () => {\n        await expect(loginPasswordPage.headline).toBeVisible();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "e2e/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2016\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"strict\": true,\n  }\n}\n"
  },
  {
    "path": "frontend/.dockerignore",
    "content": "node_modules\nnpm-debug.log\n"
  },
  {
    "path": "frontend/Dockerfile",
    "content": "FROM --platform=$BUILDPLATFORM node:18.14-alpine AS build\nRUN apk add --no-cache libc6-compat\nRUN apk update\n\nRUN npm install turbo --global\n\nWORKDIR /app\nENV PATH=/app/node_modules/.bin:$PATH\n\nCOPY package.json ./\nCOPY package-lock.json ./\nCOPY ./frontend-sdk/package.json ./frontend-sdk/package.json\nCOPY ./elements/package.json ./elements/package.json\n\nRUN npm ci --silent\n\nCOPY . .\nRUN npm run build:elements\n\nFROM nginx:stable-alpine\nCOPY --from=build /app/elements/dist/elements.js /usr/share/nginx/html\nCOPY --from=build /app/frontend-sdk/dist/sdk.* /usr/share/nginx/html\n\nCOPY elements/nginx/default.conf /etc/nginx/conf.d/default.conf\nEXPOSE 80\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n"
  },
  {
    "path": "frontend/Dockerfile.debug",
    "content": "FROM --platform=$BUILDPLATFORM node:18.14-alpine AS build\nRUN apk add --no-cache libc6-compat\nRUN apk update\n\nRUN npm install turbo --global\n\nWORKDIR /app\nENV PATH=/app/node_modules/.bin:$PATH\n\nCOPY package.json ./\nCOPY package-lock.json ./\nCOPY ./frontend-sdk/package.json ./frontend-sdk/package.json\nCOPY ./elements/package.json ./elements/package.json\n\nRUN npm ci --silent\n\nCOPY . .\nRUN npm run build:elements:dev\n\nFROM nginx:stable-alpine\nCOPY --from=build /app/elements/dist/elements.js /usr/share/nginx/html\nCOPY --from=build /app/frontend-sdk/dist/sdk.* /usr/share/nginx/html\n\nCOPY elements/nginx/default.conf /etc/nginx/conf.d/default.conf\nEXPOSE 80\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n"
  },
  {
    "path": "frontend/elements/.dockerignore",
    "content": "node_modules\ndist\n"
  },
  {
    "path": "frontend/elements/.eslintignore",
    "content": "node_modules\n"
  },
  {
    "path": "frontend/elements/.eslintrc.cjs",
    "content": "module.exports = {\n  'env': {\n    'browser': true,\n    'es2021': true,\n    'node': true,\n  },\n  'extends': [\n    'eslint:recommended',\n    'google',\n    'preact',\n    'plugin:promise/recommended',\n    // \"plugin:@typescript-eslint/recommended\",\n    // \"plugin:@typescript-eslint/recommended-requiring-type-checking\",\n    'plugin:prettier/recommended',\n  ],\n  'parser': '@typescript-eslint/parser',\n  'parserOptions': {\n    'ecmaVersion': 'latest',\n    'sourceType': 'module',\n    \"project\": \"tsconfig.json\",\n    \"tsconfigRootDir\": \".\",\n  },\n  'plugins': [\n    '@typescript-eslint'\n  ],\n  'rules': {\n    'no-unused-vars': ['error', { 'args': 'none' }]\n  }\n};\n"
  },
  {
    "path": "frontend/elements/.nvmrc",
    "content": "v20.16.0\n"
  },
  {
    "path": "frontend/elements/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Hanko GmbH\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "frontend/elements/README.md",
    "content": "# Hanko Elements\n\nProvides web components that will bring a modern login and registration experience\nto your users. It integrates the [Hanko API](https://github.com/teamhanko/hanko/blob/main/backend/README.md), a backend\nthat provides the underlying functionalities.\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Importing the Module](#importing-the-module)\n  - [Registering the Web Components](#registering-the-web-components)\n  - [Embedding the Web Components](#embedding-the-web-components)\n  - [Using the Frontend-SDK](#using-the-frontend-sdk)\n- [UI Customization](#ui-customization)\n  - [CSS Variables](#css-variables)\n  - [CSS Shadow Parts](#css-shadow-parts)\n  - [CSS Classes (not recommended)](#css-classes-not-recommended)\n- [Translations](#translations)\n  - [Default Behavior](#default-behavior)\n  - [Installing Additional Translations](#installing-additional-translations)\n  - [Modifying Translations](#modifying-translations)\n  - [Adding New Translations](#adding-new-translations)\n  - [Using External Files](#using-external-files)\n  - [Fallback Language](#fallback-language)\n- [Live Demo](#live-demo)\n- [Examples](#examples)\n- [Frontend framework integrations](#frontend-framework-integrations)\n- [Exports](#exports)\n  - [Functions](#functions)\n  - [Interfaces](#interfaces)\n- [Browser support](#browser-support)\n- [Bugs](#bugs)\n- [License](#license)\n\n## Features\n\n- User Authentication: Provides a secure and user-friendly way to handle user authentication within web applications.\n- Authentication Flows: Supports various authentication flows, including passwordless authentication and biometric\n  authentication.\n- Web Component Library: Offers customizable web components that can be easily integrated into web applications.\n- Profile Management: Allows users to view and manage their profile information through the profile component.\n- Event Handling: Provides event listeners for authentication and session-related events, enabling customization and\n  control over the user experience.\n- Localization and Internationalization: Supports multiple languages and provides translation options for a global user\n  base.\n- Integration Flexibility: Offers versatile choices for integration, including CDN or npm. It supports both TypeScript\n  and non-TypeScript environments, allowing flexibility based on the project requirements.\n- Customization: Enables customization of visual styles, branding, and user interface elements to align with the overall\n  application design.\n- Documentation and Support: Offers documentation, example apps, frontend framework integration guides and support via\n  Slack to assist with integration and troubleshooting.\n\n## Installation\n\nTo use the Hanko Elements module in your project, you can install it via npm, yarn, or pnpm. Alternatively, you can also\n[import the module](#importing-the-module) directly via a CDN.\n\n```shell\n# npm\nnpm install @teamhanko/hanko-elements\n\n# yarn\nyarn add @teamhanko/hanko-elements\n\n# pnpm\npnpm install @teamhanko/hanko-elements\n```\n\n## Usage\n\nTo integrate Hanko, you need to import and call the `register()` function from the `hanko-elements` module. Once this is\ndone, you can use the web components in your HTML code. For a functioning page, at least the `<hanko-auth>` element\nshould be placed, so the users can sign in, and also, a handler for the \"onSessionCreated\" event should be added, to\ncustomize the behaviour after the authentication flow has been completed (e.g. redirect to another page). These steps\nwill be described in the following sections.\n\n### Importing the Module\n\nTo use the web components, you need to register them using the `register()` function provided by the `hanko-elements`\npackage.\n\nIf you're using a module bundler like webpack or Parcel, you can import the `register()` function from the\n`@teamhanko/hanko-elements` package in your TypeScript or JavaScript file:\n\n```typescript\nimport { register } from \"@teamhanko/hanko-elements\";\n```\n\nIf you prefer using a CDN, you can include a script tag with the import statement pointing to the CDN URL where the\n`hanko-elements` package is hosted:\n\n```html\n<script type=\"module\">\n  import { register } from \"https://cdn.jsdelivr.net/npm/@teamhanko/hanko-elements/dist/elements.js\";\n</script>\n```\n\n### Registering the Web Components\n\nAfter importing the `register()` function, call it with the URL of the Hanko API as an argument to register the Hanko\nelements with the browser's `CustomElementRegistry`.\n\n```javascript\nconst { hanko } = await register(\"https://hanko.yourdomain.com\");\n```\n\nYou can also pass certain options:\n\n```javascript\nconst defaultOptions = {\n  shadow: true,                    // Set to false if you do not want the web component to be attached to the shadow DOM.\n  injectStyles: true,              // Set to false if you do not want to inject any default styles.\n  enablePasskeys: true,            // Set to false if you do not want to display passkey-related content.\n  hidePasskeyButtonOnLogin: false, // Hides the button to sign in with a passkey on the login page.\n  translations: null,              // Additional translations can be added here. English is used when the option is not\n                                   // present or set to `null`, whereas setting an empty object `{}` prevents the elements\n                                   // from displaying any translations.\n  translationsLocation: \"/i18n\",   // The URL or path where the translation files are located.\n  fallbackLanguage: \"en\",          // The fallback language to be used if a translation is not available.\n  storageKey: \"hanko\",             // The name of the cookie the session token is stored in and the prefix / name of local storage keys\n  cookieDomain: undefined,          // The domain where the cookie set from the SDK is available. When undefined,\n                                   // defaults to the domain of the page where the cookie was created.\n  cookieSameSite: \"lax\",           // Specify whether/when cookies are sent with cross-site requests.\n  sessionCheckInterval: 30000,     // Interval for session validity checks in milliseconds. Must be greater than 3000 (3s).\n};\n\nconst { hanko } = await register(\n  \"https://hanko.yourdomain.com\",\n  defaultOptions\n);\n```\n\nReplace \"https://hanko.yourdomain.com\" with the actual URL of your Hanko API.\n\n### Embedding the Web Components\n\nIf you have followed the steps mentioned above, you should now be able to place the web components anywhere in the body\nof your HTML. A minimal example would look like this:\n\n```html\n<hanko-auth id=\"authComponent\"></hanko-auth>\n\n<script type=\"module\">\n  import { register } from \"https://cdn.jsdelivr.net/npm/@teamhanko/hanko-elements/dist/elements.js\";\n\n  await register(\"https://hanko.yourdomain.com\");\n\n  const authComponent = document.getElementById(\"authComponent\");\n  authComponent.addEventListener(\"onSessionCreated\", () => {\n    // redirect to a different page\n  });\n</script>\n```\n\nThe individual web component are described in the following sections.\n\n#### &lt;hanko-auth&gt;, &lt;hanko-login&gt; and &lt;hanko-registration&gt;\n\nThese three web components offer a user-friendly interface for user login or registration. The difference between\nthe components is, that `<hanko-auth>` has the ability to switch between the login and\nregistration UI, whereas `<hanko-login>` is dedicated to the login and `<hanko-registration>`\nto the registration only.\n\n##### Markup\n\nCombined UI for login and registration:\n\n```html\n<hanko-auth></hanko-auth>\n```\n\nDedicated UI for the login:\n\n```html\n<hanko-login></hanko-login>\n```\n\nDedicated UI for the registration:\n\n```html\n<hanko-registration></hanko-registration>\n```\n\n##### Attributes\n\n- `prefilled-email` Used to prefill the email input field.\n- `prefilled-username` Used to prefill the username input field.\n- `lang` Used to specify the language of the content within the element. See [Translations](#translations).\n- `mode` Accepts either \"login\" or \"registration\" to initialize the `<hanko-auth>` component with a login or registration flow, respectively.\n- `nonce` A nonce that is used to allow loading of inline styles when a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy) is applied.\n\n#### &lt;hanko-profile&gt;\n\nA web component that allows to manage emails, passwords and passkeys.\n\n##### Markup\n\n```html\n<hanko-profile></hanko-profile>\n```\n\n##### Attributes\n\n- `lang` Used to specify the language of the content within the element. See [Translations](#translations).\n- `nonce` A nonce that is used to allow loading of inline styles when a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy) is applied.\n\n#### &lt;hanko-events&gt;\n\nA web component that allows to bind event handler to certain events, without displaying UI elements. Events can be\nsubscribed to with the `<hanko-auth>` and `<hanko-profile>` components in the same manner. Also, you can bind event\nhandler via the `frontend-sdk` (see next section).\n\n##### Markup\n\n```html\n<hanko-events id=\"events\"></hanko-events>\n<script>\n  document\n    .getElementById(\"events\")\n    .addEventListener(\"onSessionCreated\", console.log);\n  // more events are available (see \"frontend-sdk\" docs)...\n</script>\n```\n\n### Using the Frontend-SDK\n\nThe following examples will cover some common use-cases for the `hanko-frontend-sdk` instance returned by\nthe `register()` function:\n\n```js\n// Validate the current session\nconst session = await hanko.validateSession();\nconsole.log(\"Session valid:\", session.is_valid, \"Claims:\", session.claims);\n\n// Retrieve the session token\nconst token = hanko.getSessionToken();\nconsole.log(\"Session token:\", token);\n\n// Fetch the user profile\nconst user = await hanko.getUser();\nconsole.log(\"User profile:\", user.user_id, user.emails);\n\n// Log out the user\nawait hanko.logout();\nconsole.log(\"User logged out\");\n\n// Handle session creation event\nhanko.onSessionCreated(({ claims }) => {\n    console.log(\"Session created with JWT claims:\", claims);\n});\n\n// Handle session expiration event\nhanko.onSessionExpired(() => {\n    console.log(\"Session expired, redirecting to login\");\n});\n\n// Handle user logout event\nhanko.onUserLoggedOut(() => {\n    console.log(\"User logged out successfully\");\n});\n\n// Handle user account deletion event\nhanko.onUserDeleted(() => {\n    console.log(\"User account deleted\");\n});\n```\n\nPlease take a look into the [frontend-sdk docs](https://teamhanko.github.io/hanko/jsdoc/hanko-frontend-sdk/), for\nfurther details.\n\n## UI Customization\n\n### CSS Variables\n\nCSS variables can be used to style the `hanko-auth` and `hanko-profile` elements to your needs. A list of all CSS\nvariables including default values can be found below:\n\n```css\nhanko-auth,\nhanko-profile {\n  /* Color Scheme */\n  --color: #333333;\n  --color-shade-1: #8f9095;\n  --color-shade-2: #e5e6ef;\n\n  --brand-color: #506cf0;\n  --brand-color-shade-1: #6b84fb;\n  --brand-contrast-color: white;\n\n  --background-color: white;\n  --error-color: #e82020;\n  --link-color: #506cf0;\n\n  /* Font Styles */\n  --font-weight: 400;\n  --font-size: 16px;\n  --font-family: sans-serif;\n\n  /* Border Styles */\n  --border-radius: 8px;\n  --border-style: solid;\n  --border-width: 1px;\n\n  /* Item Styles */\n  --item-height: 34px;\n  --item-margin: 0.5rem 0;\n\n  /* Container Styles */\n  --container-padding: 30px;\n  --container-max-width: 410px;\n\n  /* Headline Styles */\n  --headline1-font-size: 24px;\n  --headline1-font-weight: 600;\n  --headline1-margin: 0 0 1rem;\n\n  --headline2-font-size: 16px;\n  --headline2-font-weight: 600;\n  --headline2-margin: 1rem 0 0.5rem;\n\n  /* Divider Styles */\n  --divider-padding: 0 42px;\n  --divider-visibility: visible;\n\n  /* Link Styles */\n  --link-text-decoration: none;\n  --link-text-decoration-hover: underline;\n\n  /* Input Styles */\n  --input-min-width: 14em;\n\n  /* Button Styles */\n  --button-min-width: max-content;\n}\n```\n\n### CSS Shadow Parts\n\nIn addition to CSS variables, you can utilize the `::part` selector to customize the styles of various elements.\n\nPlease note that shadow parts only function when the web components are attached to the shadow DOM, which is the default\nbehavior. You can enable the shadow DOM for the components using the following code snippet:\n\n```javascript\nregister(\"https://hanko.yourdomain.com\", { shadow: true });\n\n// equals\n\nregister(\"https://hanko.yourdomain.com\");\n```\n\n#### List of all shadow parts\n\nThe following parts are available:\n\n- `container` - the UI container\n- `headline1` - the \"h1\" headlines\n- `headline2` - the \"h2\" headlines\n- `paragraph` - the paragraph elements\n- `button` - every button element\n- `primary-button` - the primary button\n- `secondary-button` - the secondary button on the email login page\n- `input` - every input field\n- `text-input` - every input field not used for passcodes\n- `passcode-input` - the passcode input fields\n- `link` - the links in the footer section\n- `error` - the error message container\n- `error-text` - the error message\n- `divider` - the horizontal divider on the login page\n- `divider-text` - the divider text\n- `divider-line` - the line before and after the `divider-text`\n- `form-item` - the container of a form item, e.g. an input field or a button\n\n#### Using shadow parts\n\nThe following examples demonstrate how to apply styles to specific shadow parts:\n\n##### Example 1\n\nThis example demonstrates how to force the input fields and buttons within the `hanko-auth` component to stack\nvertically. The `::part(form-item)` selector targets the `form-item` shadow part within the hanko-auth component, which\nis applied using the tag name.\n\n```html\n<style>\n  hanko-auth::part(form-item) {\n    /* Force the input fields and buttons are on top of each other */\n    min-width: 100%;\n  }\n</style>\n\n<hanko-auth></hanko-auth>\n```\n\n##### Example 2\n\nThis example shows how to adjust the main headlines for all hanko components by targeting the `headline1` shadow part.\nThe `.hankoComponent::part(headline1)` selector applies the styles to the `headline1` shadow part within elements that\nhave the `hankoComponent` class.\n\n```html\n<style>\n  .hankoComponent::part(headline1) {\n    /* Adjust the main headlines for all hanko components */\n    font-size: 1.3em;\n    font-weight: 400;\n  }\n</style>\n\n<hanko-auth class=\"hankoComponent\"></hanko-auth>\n<hanko-profile class=\"hankoComponent\"></hanko-profile>\n```\n\n##### Example 3\n\nIn this example, a box shadow is applied to the `button` shadow part of the `hanko-auth` component when hovering over\nit. The `#hankoAuth::part(button):hover` selector targets the button shadow part within the hanko-auth component using\nthe ID selector `#hankoAuth` and applies the styles when the `:hover` pseudo-class is active.\n\n```html\n<style>\n  #hankoAuth::part(button):hover {\n    box-shadow: 3px 3px 2px #888;\n  }\n</style>\n\n<hanko-auth id=\"hankoAuth\"></hanko-auth>\n```\n\n### CSS Classes (not recommended)\n\nThere is also the possibility to provide your own CSS rules when the web component has not been attached to the shadow\nDOM:\n\n```typescript\nregister(\"https://hanko.yourdomain.com\", { shadow: false });\n```\n\nPlease take a look at the [CSS example](https://github.com/teamhanko/hanko/raw/main/frontend/elements/example.css) file\nto see\nwhich CSS rules can be used. If you only want to change specific properties you can override the predefined ones. For\nexample if you like to change the background color, include the following CSS rule:\n\n```css\n.hanko_container {\n  background-color: blue !important;\n}\n```\n\nAlso, you can prevent injecting any styles:\n\n```typescript\nregister(\"https://hanko.yourdomain.com\", {\n  shadow: false,\n  injectStyles: false,\n});\n```\n\nso you don't need to override properties but provide the entirety of CSS rules:\n\n```css\n.hanko_container {\n  background-color: blue;\n}\n\n/* more css rules... */\n```\n\nIf this is your preferred approach, start with\nthe [CSS example](https://github.com/teamhanko/hanko/raw/main/frontend/elements/example.css)\nfile, change everything according to your needs and include the CSS in your page.\n\nKeep in mind we made CSS classes available and added light DOM support only because a Safari bug is breaking the\nautocompletion of input elements while the web component is attached to the shadow DOM. You would normally prefer to\nattach the component to the shadow DOM and make use of CSS parts for UI customization when the CSS variables are not\nsufficient.\n\n## Translations\n\n### Default Behavior\n\nThe `hanko-elements` package includes English translations by default and the `lang` attribute can be omitted.\n\nScript:\n\n```typescript\nregister(\"https://hanko.yourdomain.com\");\n```\n\nMarkup:\n\n```html\n<hanko-auth></hanko-auth>\n```\n\n### Installing Additional Translations\n\nTranslations are currently available for the following languages:\n\n- \"bn\" - Bengali\n- \"de\" - German\n- \"en\" - English\n- \"fr\" - French\n- \"it\" - Italian\n- \"nl\" - Dutch\n- \"ptBR\" - Brazilian Portuguese\n- \"zh\" - Simplified Chinese\n\nYou can import them individually:\n\n```typescript\n// Replace the paths below with\n// \"https://cdn.jsdelivr.net/npm/@teamhanko/hanko-elements/dist/i18n/{en|de|all|...}.js\"\n// if you're using CDN.\n\nimport { bn } from \"@teamhanko/hanko-elements/i18n/bn\";\nimport { de } from \"@teamhanko/hanko-elements/i18n/de\";\nimport { en } from \"@teamhanko/hanko-elements/i18n/en\";\nimport { fr } from \"@teamhanko/hanko-elements/i18n/fr\";\nimport { it } from \"@teamhanko/hanko-elements/i18n/it\";\nimport { nl } from \"@teamhanko/hanko-elements/i18n/nl\";\nimport { ptBR } from \"@teamhanko/hanko-elements/i18n/pt-BR\";\nimport { zh } from \"@teamhanko/hanko-elements/i18n/zh\";\n```\n\nOr import all translations at once:\n\n```typescript\nimport { all } from \"@teamhanko/hanko-elements/i18n/all\";\n```\n\nAfter importing, provide the translations through the `register()` function:\n\n```typescript\nregister(\"https://hanko.yourdomain.com\", { translations: { bn, de, en, fr, it, nl, ptBR, zh } });\n\n// or\n\nregister(\"https://hanko.yourdomain.com\", { translations: all });\n```\n\nYou can now set the `lang` attribute of the element to the desired language:\n\n```html\n<hanko-auth lang=\"de\"></hanko-auth>\n```\n\n### Modifying Translations\n\nYou can modify existing translations as follows:\n\n```typescript\nimport { en } from \"@teamhanko/hanko-elements/i18n/en\";\n\nen.errors.somethingWentWrong = \"Aww, snap!\";\n\nregister(\"https://hanko.yourdomain.com\", { translations: { en } });\n```\n\n### Adding New Translations\n\nIf you need to create a new translation, pass an object that implements (or partially implements) the `Translation`\ninterface.\n\nScript:\n\n```typescript\nimport { all } from \"@teamhanko/hanko-elements/i18n/all\";\nimport { Translation } from \"@teamhanko/hanko-elements\"; // if you're using typescript\n\nconst myLang: Translation = {...}\n\nregister(\"https://hanko.yourdomain.com\", {translations: {...all, myLang}});\n```\n\nMarkup:\n\n```html\n<hanko-auth lang=\"myLang\"></hanko-auth>\n```\n\n### Using External Files\n\nFor languages provided via the element's `lang` attribute, or via the [fallback language](#fallback-language) option,\nthat are not included in the object passed to the `translations` option, the component will fetch a JSON file from the\nlocation specified by the `translationsLocation` option. For example, if \"en\" is missing due to an empty object being\npassed, as shown in the example below, the component will fetch a file named \"/i18n/en.json\".\n\nScript:\n\n```typescript\nregister(\"https://hanko.yourdomain.com\", {\n  translations: {}, // An empty object, so even the default \"en\" translation won't be available.\n  translationsLocation: \"/i18n\", // A public folder containing language files, e.g., \"en.json\".\n});\n```\n\nMarkup:\n\n```html\n<!-- Will fetch \"/i18n/en.json\" -->\n<hanko-auth lang=\"en\"></hanko-auth>\n```\n\n### Fallback Language\n\nThe `fallbackLanguage` option is used to specify a fallback language for the web components when translations are\nmissing or incomplete for a particular language. By setting the `fallbackLanguage` option to a valid language string\nlike \"en\" or \"de\", the missing translation strings will be automatically retrieved from the specified fallback language.\nWhen the translation for the specified `fallbackLanguage` is not available in the `translations` option, the\nweb components will attempt to fetch it from an [external file](#using-external-files).\n\nScript:\n\n```typescript\nimport { en } from \"@teamhanko/hanko-elements/i18n/en\";\nimport { Translation } from \"@teamhanko/hanko-elements\";\n\nconst symbols: Partial<Translation> = {\n  labels: { continue: \"➔\" },\n};\n\nregister(\"https://hanko.yourdomain.com\", {\n  fallbackLanguage: \"en\",\n  translations: { en, symbols },\n});\n```\n\nMarkup:\n\n```html\n<!-- Will appear in English, but the \"continue\" button label will be \"➔\"  -->\n<hanko-auth lang=\"symbols\"></hanko-auth>\n```\n\n### Translation of outgoing Hanko emails\n\nIf you use Hanko Elements the language supplied to the `lang` attribute of any of the components is also used to convey\nto the Hanko API the language to use for outgoing emails. If you have disabled email delivery through Hanko and\nconfigured a webhook for the `email.send` event, the value for the `lang` attribute is reflected in the JWT payload of\nthe token contained in the webhook request in the `language` claim.\n\n## Live Demo\n\nTake a look at our [live demo](https://example.hanko.io).\n\n## Examples\n\nThe following example implementations are currently available, demonstrating integration into both vanilla JavaScript\nand frontend framework environments:\n\n- [A single HTML file](https://raw.githubusercontent.com/teamhanko/hanko/main/frontend/elements/src/example.html) that\n  implements most of the features mentioned on this page, with all the key details explained in the comments. You can host\n  it on any HTTP server, including locally.\n- [Todo apps](https://github.com/teamhanko/hanko/blob/main/frontend/examples/README.md) that demonstrate how integration\n  works in various frontend frameworks and provide insights on managing backend communication and JWT validation.\n\n## Frontend framework integrations\n\nTo learn more about how to integrate the Hanko elements into frontend frameworks, see our\n[guides](https://docs.hanko.io/guides/frontend) in the official documentation and our\n[example applications](https://github.com/teamhanko/hanko/blob/main/frontend/examples/README.md).\n\n## Exports\n\nThe `@teamhanko/hanko-elements` package exports the functions and interfaces listed below and additionally every\ndeclaration provided by the [frontend-sdk](https://teamhanko.github.io/hanko/jsdoc/hanko-frontend-sdk/).\n\n### Functions\n\n- `register` - A function to register the web components with the browser's custom element registry.\n\n### Interfaces\n\n- `RegisterOptions` - represents the options of the `register()` function.\n- `RegisterResult` - represents the return value of the `register()` function.\n- `Translation` - represents a translation that can be provided through the `RegisterOptions`.\n- `HankoAuthElementProps` - represents the `<hanko-auth>` element properties.\n- `HankoProfileElementProps` - represents the `<hanko-profile>` element properties.\n- `HankoEventsElementProps` - represents the `<hanko-events>` element properties.\n\n## Browser support\n\n- Safari\n- Firefox\n- Opera\n- Chromium-based browsers (Chrome, Edge, Brave,...)\n\n## Bugs\n\n- Customizable UI: In Chrome the `::part` selector is not working in combination with some pseudo classes.\n  E.g. `:disabled` is currently broken. See:\n  [chromium-issue-#1131396](https://bugs.chromium.org/p/chromium/issues/detail?id=1131396),\n  [chromium-issue-#953648](https://bugs.chromium.org/p/chromium/issues/detail?id=953648)\n\nFound a bug? Please report on our [GitHub](https://github.com/teamhanko/hanko/issues) page.\n\n## License\n\nThe `elements` project is licensed under the [MIT License](LICENSE).\n"
  },
  {
    "path": "frontend/elements/babel.config.cjs",
    "content": "module.exports = {\n  presets: [\n    [\n      '@babel/preset-env',\n      {\n        targets: {\n          node: 'current',\n        },\n      }\n    ],\n    '@babel/preset-typescript',\n  ],\n};\n"
  },
  {
    "path": "frontend/elements/example.css",
    "content": ".hanko_container {\n    background-color: var(--background-color, white);\n    padding: var(--container-padding, 30px);\n    max-width: var(--container-max-width, 410px);\n    display: flex;\n    flex-direction: column;\n    flex-wrap: nowrap;\n    justify-content: center;\n    align-items: center;\n    align-content: flex-start;\n    box-sizing: border-box\n}\n\n.hanko_content {\n    box-sizing: border-box;\n    flex: 0 1 auto;\n    width: 100%;\n    height: 100%\n}\n\n.hanko_footer {\n    padding: .5rem 0 0;\n    box-sizing: border-box;\n    width: 100%\n}\n\n.hanko_footer :nth-child(1) {\n    float: left\n}\n\n.hanko_footer :nth-child(2) {\n    float: right\n}\n\n.hanko_clipboardContainer {\n    display: flex\n}\n\n.hanko_clipboardIcon {\n    display: flex;\n    margin: auto;\n    cursor: pointer\n}\n\n.hanko_icon,\n.hanko_loadingSpinnerWrapper .hanko_loadingSpinner,\n.hanko_loadingSpinnerWrapperIcon .hanko_loadingSpinner,\n.hanko_exclamationMark,\n.hanko_checkmark {\n    display: inline-block;\n    fill: var(--brand-contrast-color, white);\n    width: 18px\n}\n\n.hanko_icon.hanko_secondary,\n.hanko_loadingSpinnerWrapper .hanko_secondary.hanko_loadingSpinner,\n.hanko_loadingSpinnerWrapperIcon .hanko_secondary.hanko_loadingSpinner,\n.hanko_secondary.hanko_exclamationMark,\n.hanko_secondary.hanko_checkmark {\n    fill: var(--color, #333333)\n}\n\n.hanko_icon.hanko_disabled,\n.hanko_loadingSpinnerWrapper .hanko_disabled.hanko_loadingSpinner,\n.hanko_loadingSpinnerWrapperIcon .hanko_disabled.hanko_loadingSpinner,\n.hanko_disabled.hanko_exclamationMark,\n.hanko_disabled.hanko_checkmark {\n    fill: var(--color-shade-1, #8f9095)\n}\n\n.hanko_checkmark {\n    fill: var(--brand-color, #506cf0)\n}\n\n.hanko_checkmark.hanko_secondary {\n    fill: var(--color-shade-1, #8f9095)\n}\n\n.hanko_checkmark.hanko_fadeOut {\n    animation: hanko_fadeOut ease-out 1.5s forwards !important\n}\n\n@keyframes hanko_fadeOut {\n    0% {\n        opacity: 1\n    }\n\n    100% {\n        opacity: 0\n    }\n}\n\n.hanko_exclamationMark {\n    fill: var(--error-color, #e82020)\n}\n\n.hanko_loadingSpinnerWrapperIcon {\n    width: 100%;\n    column-gap: 10px;\n    margin-left: 10px\n}\n\n.hanko_loadingSpinnerWrapper,\n.hanko_loadingSpinnerWrapperIcon {\n    display: inline-flex;\n    align-items: center;\n    height: 100%;\n    margin: 0 5px;\n    justify-content: inherit\n}\n\n.hanko_loadingSpinnerWrapper.hanko_centerContent,\n.hanko_centerContent.hanko_loadingSpinnerWrapperIcon {\n    justify-content: center\n}\n\n.hanko_loadingSpinnerWrapper.hanko_maxWidth,\n.hanko_maxWidth.hanko_loadingSpinnerWrapperIcon {\n    width: 100%\n}\n\n.hanko_loadingSpinnerWrapper .hanko_loadingSpinner,\n.hanko_loadingSpinnerWrapperIcon .hanko_loadingSpinner {\n    fill: var(--brand-color, #506cf0);\n    animation: hanko_spin 500ms ease-in-out infinite\n}\n\n.hanko_loadingSpinnerWrapper.hanko_secondary,\n.hanko_secondary.hanko_loadingSpinnerWrapperIcon {\n    fill: var(--color-shade-1, #8f9095)\n}\n\n@keyframes hanko_spin {\n    0% {\n        transform: rotate(0deg)\n    }\n\n    100% {\n        transform: rotate(360deg)\n    }\n}\n\n.hanko_googleIcon.hanko_disabled {\n    fill: var(--color-shade-1, #8f9095)\n}\n\n.hanko_googleIcon.hanko_blue {\n    fill: #4285f4\n}\n\n.hanko_googleIcon.hanko_green {\n    fill: #34a853\n}\n\n.hanko_googleIcon.hanko_yellow {\n    fill: #fbbc05\n}\n\n.hanko_googleIcon.hanko_red {\n    fill: #ea4335\n}\n\n.hanko_microsoftIcon.hanko_disabled {\n    fill: var(--color-shade-1, #8f9095)\n}\n\n.hanko_microsoftIcon.hanko_blue {\n    fill: #00a4ef\n}\n\n.hanko_microsoftIcon.hanko_green {\n    fill: #7fba00\n}\n\n.hanko_microsoftIcon.hanko_yellow {\n    fill: #ffb900\n}\n\n.hanko_microsoftIcon.hanko_red {\n    fill: #f25022\n}\n\n.hanko_form {\n    display: flex;\n    flex-grow: 1\n}\n\n.hanko_form .hanko_ul {\n    flex-grow: 1;\n    margin: var(--item-margin, 0.5rem 0);\n    padding-inline-start: 0;\n    list-style-type: none;\n    display: flex;\n    flex-wrap: wrap;\n    gap: 1em\n}\n\n.hanko_form .hanko_li {\n    display: flex;\n    max-width: 100%;\n    flex-grow: 1;\n    flex-basis: min-content\n}\n\n.hanko_form .hanko_li.hanko_maxWidth {\n    min-width: 100%\n}\n\n.hanko_button {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    border-radius: var(--border-radius, 8px);\n    border-style: var(--border-style, solid);\n    border-width: var(--border-width, 1px);\n    white-space: nowrap;\n    width: 100%;\n    min-width: var(--button-min-width, 7em);\n    height: var(--item-height, 42px);\n    outline: none;\n    cursor: pointer;\n    transition: .1s ease-out;\n    flex-grow: 1;\n    flex-shrink: 1;\n    display: inline-flex\n}\n\n.hanko_button:disabled {\n    cursor: default\n}\n\n.hanko_button.hanko_primary {\n    color: var(--brand-contrast-color, white);\n    background: var(--brand-color, #506cf0);\n    border-color: var(--brand-color, #506cf0);\n    justify-content: center\n}\n\n.hanko_button.hanko_primary:hover {\n    color: var(--brand-contrast-color, white);\n    background: var(--brand-color-shade-1, #6b84fb);\n    border-color: var(--brand-color, #506cf0)\n}\n\n.hanko_button.hanko_primary:focus {\n    color: var(--brand-contrast-color, white);\n    background: var(--brand-color, #506cf0);\n    border-color: var(--color, #333333)\n}\n\n.hanko_button.hanko_primary:disabled {\n    color: var(--color-shade-1, #8f9095);\n    background: var(--color-shade-2, #e5e6ef);\n    border-color: var(--color-shade-2, #e5e6ef)\n}\n\n.hanko_button.hanko_secondary {\n    color: var(--color, #333333);\n    background: var(--background-color, white);\n    border-color: var(--color, #333333);\n    justify-content: left\n}\n\n.hanko_button.hanko_secondary:hover {\n    color: var(--color, #333333);\n    background: var(--color-shade-2, #e5e6ef);\n    border-color: var(--color, #333333)\n}\n\n.hanko_button.hanko_secondary:focus {\n    color: var(--color, #333333);\n    background: var(--background-color, white);\n    border-color: var(--brand-color, #506cf0)\n}\n\n.hanko_button.hanko_secondary:disabled {\n    color: var(--color-shade-1, #8f9095);\n    background: var(--color-shade-2, #e5e6ef);\n    border-color: var(--color-shade-1, #8f9095)\n}\n\n.hanko_button.hanko_dangerous {\n    color: var(--error-color, #e82020);\n    background: var(--background-color, white);\n    border-color: var(--error-color, #e82020);\n    flex-grow: 0;\n    width: auto\n}\n\n.hanko_inputWrapper {\n    flex-grow: 1;\n    position: relative;\n    display: flex;\n    min-width: var(--input-min-width, 14em);\n    max-width: 100%\n}\n\n.hanko_input {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    border-radius: var(--border-radius, 8px);\n    border-style: var(--border-style, solid);\n    border-width: var(--border-width, 1px);\n    height: var(--item-height, 42px);\n    color: var(--color, #333333);\n    border-color: var(--color-shade-1, #8f9095);\n    background: var(--background-color, white);\n    padding: 0 .5rem;\n    outline: none;\n    width: 100%;\n    box-sizing: border-box;\n    transition: .1s ease-out\n}\n\n.hanko_input.hanko_error {\n    border-color: var(--error-color, #e82020)\n}\n\n.hanko_input:-webkit-autofill,\n.hanko_input:-webkit-autofill:hover,\n.hanko_input:-webkit-autofill:focus {\n    -webkit-text-fill-color: var(--color, #333333);\n    -webkit-box-shadow: 0 0 0 50px var(--background-color, white) inset\n}\n\n.hanko_input::-ms-reveal,\n.hanko_input::-ms-clear {\n    display: none\n}\n\n.hanko_input::placeholder {\n    color: var(--color-shade-1, #8f9095)\n}\n\n.hanko_input:focus {\n    color: var(--color, #333333);\n    border-color: var(--color, #333333)\n}\n\n.hanko_input:disabled {\n    color: var(--color-shade-1, #8f9095);\n    background: var(--color-shade-2, #e5e6ef);\n    border-color: var(--color-shade-1, #8f9095)\n}\n\n.hanko_passcodeInputWrapper {\n    flex-grow: 1;\n    min-width: var(--input-min-width, 14em);\n    max-width: fit-content;\n    position: relative;\n    display: flex;\n    justify-content: space-between\n}\n\n.hanko_passcodeInputWrapper .hanko_passcodeDigitWrapper {\n    flex-grow: 1;\n    margin: 0 .5rem 0 0\n}\n\n.hanko_passcodeInputWrapper .hanko_passcodeDigitWrapper:last-child {\n    margin: 0\n}\n\n.hanko_passcodeInputWrapper .hanko_passcodeDigitWrapper .hanko_input {\n    text-align: center\n}\n\n.hanko_checkboxWrapper {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    color: var(--color, #333333);\n    align-items: center;\n    display: flex\n}\n\n.hanko_checkboxWrapper .hanko_label {\n    color: inherit;\n    padding-left: .5rem;\n    cursor: pointer\n}\n\n.hanko_checkboxWrapper .hanko_label.hanko_disabled {\n    cursor: default;\n    color: var(--color-shade-1, #8f9095)\n}\n\n.hanko_checkboxWrapper .hanko_checkbox {\n    border: currentColor solid 1px;\n    border-radius: .15em;\n    appearance: none;\n    -webkit-appearance: none;\n    width: 1.1rem;\n    height: 1.1rem;\n    margin: 0;\n    color: currentColor;\n    background-color: var(--background-color, white);\n    font: inherit;\n    box-shadow: none;\n    display: inline-flex;\n    place-content: center;\n    cursor: pointer\n}\n\n.hanko_checkboxWrapper .hanko_checkbox:checked {\n    background-color: var(--color, #333333)\n}\n\n.hanko_checkboxWrapper .hanko_checkbox:disabled {\n    cursor: default;\n    background-color: var(--color-shade-2, #e5e6ef);\n    border-color: var(--color-shade-1, #8f9095)\n}\n\n.hanko_checkboxWrapper .hanko_checkbox:checked:after {\n    content: \"✓\";\n    color: var(--background-color, white);\n    position: absolute;\n    line-height: 1.1rem\n}\n\n.hanko_checkboxWrapper .hanko_checkbox:disabled:after {\n    color: var(--color-shade-1, #8f9095)\n}\n\n.hanko_spacer {\n    height: 1em\n}\n\n.hanko_divider {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    display: flex;\n    visibility: var(--divider-visibility, visible);\n    color: var(--color-shade-1, #8f9095);\n    margin: var(--item-margin, 0.5rem 0);\n    padding: .5em 0\n}\n\n.hanko_divider .hanko_line {\n    border-bottom-style: var(--border-style, solid);\n    border-bottom-width: var(--border-width, 1px);\n    color: inherit;\n    font: inherit;\n    width: 100%\n}\n\n.hanko_divider .hanko_text {\n    font: inherit;\n    color: inherit;\n    background: var(--background-color, white);\n    padding: var(--divider-padding, 0 42px);\n    line-height: .1em\n}\n\n.hanko_errorBox {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    border-radius: var(--border-radius, 8px);\n    border-style: var(--border-style, solid);\n    border-width: var(--border-width, 1px);\n    color: var(--error-color, #e82020);\n    background: var(--background-color, white);\n    margin: var(--item-margin, 0.5rem 0);\n    display: flex;\n    align-items: start;\n    box-sizing: border-box;\n    line-height: 1.5rem;\n    padding: .25em;\n    gap: .2em\n}\n\n.hanko_errorBox>span {\n    display: inline-flex\n}\n\n.hanko_errorBox>span:first-child {\n    padding: .25em 0 .25em .19em\n}\n\n.hanko_errorBox[hidden] {\n    display: none\n}\n\n.hanko_errorMessage {\n    color: var(--error-color, #e82020)\n}\n\n.hanko_headline {\n    color: var(--color, #333333);\n    font-family: var(--font-family, sans-serif);\n    text-align: left;\n    letter-spacing: 0;\n    font-style: normal;\n    line-height: 1.1\n}\n\n.hanko_headline.hanko_grade1 {\n    font-size: var(--headline1-font-size, 24px);\n    font-weight: var(--headline1-font-weight, 600);\n    margin: var(--headline1-margin, 0 0 0.5rem)\n}\n\n.hanko_headline.hanko_grade2 {\n    font-size: var(--headline2-font-size, 16px);\n    font-weight: var(--headline2-font-weight, 600);\n    margin: var(--headline2-margin, 1rem 0 0.5rem)\n}\n\n.hanko_link {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    color: var(--link-color, #506cf0);\n    text-decoration: var(--link-text-decoration, none);\n    cursor: pointer;\n    background: none !important;\n    border: none;\n    padding: 0 !important;\n    transition: all .1s\n}\n\n.hanko_link:hover {\n    text-decoration: var(--link-text-decoration-hover, underline)\n}\n\n.hanko_link:disabled {\n    color: var(--color, #333333) !important;\n    pointer-events: none;\n    cursor: default\n}\n\n.hanko_link.hanko_danger {\n    color: var(--error-color, #e82020)\n}\n\n.hanko_linkWrapper {\n    display: inline-flex;\n    flex-direction: row;\n    justify-content: space-between;\n    align-items: center;\n    overflow: hidden\n}\n\n.hanko_linkWrapper.hanko_reverse {\n    flex-direction: row-reverse\n}\n\n.hanko_paragraph {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    color: var(--color, #333333);\n    margin: var(--item-margin, 0.5rem 0);\n    text-align: left;\n    word-break: break-word\n}\n\n.hanko_accordion {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    width: 100%;\n    overflow: hidden\n}\n\n.hanko_accordion .hanko_accordionItem {\n    color: var(--color, #333333);\n    margin: .25rem 0;\n    overflow: hidden\n}\n\n.hanko_accordion .hanko_accordionItem.hanko_dropdown {\n    margin: 0\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label {\n    border-radius: var(--border-radius, 8px);\n    border-style: none;\n    height: var(--item-height, 42px);\n    background: var(--background-color, white);\n    box-sizing: border-box;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 0 1rem;\n    margin: 0;\n    cursor: pointer;\n    transition: all .35s\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label .hanko_labelText {\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label .hanko_labelText .hanko_description {\n    color: var(--color-shade-1, #8f9095)\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label.hanko_dropdown {\n    margin: 0;\n    color: var(--link-color, #506cf0);\n    justify-content: flex-start\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label:hover {\n    color: var(--brand-contrast-color, white);\n    background: var(--brand-color-shade-1, #6b84fb)\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label:hover .hanko_description {\n    color: var(--brand-contrast-color, white)\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label:hover.hanko_dropdown {\n    color: var(--link-color, #506cf0);\n    background: none\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_label:not(.hanko_dropdown)::after {\n    content: \"❯\";\n    width: 1rem;\n    text-align: center;\n    transition: all .35s\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionInput {\n    position: absolute;\n    opacity: 0;\n    z-index: -1\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionInput:checked+.hanko_label {\n    color: var(--brand-contrast-color, white);\n    background: var(--brand-color, #506cf0)\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionInput:checked+.hanko_label .hanko_description {\n    color: var(--brand-contrast-color, white)\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionInput:checked+.hanko_label.hanko_dropdown {\n    color: var(--link-color, #506cf0);\n    background: none\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionInput:checked+.hanko_label:not(.hanko_dropdown)::after {\n    transform: rotate(90deg)\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionInput:checked+.hanko_label~.hanko_accordionContent {\n    margin: .25rem 1rem;\n    opacity: 1;\n    max-height: 100vh\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionContent {\n    max-height: 0;\n    margin: 0 1rem;\n    opacity: 0;\n    overflow: hidden;\n    transition: all .35s\n}\n\n.hanko_accordion .hanko_accordionItem .hanko_accordionContent.hanko_dropdownContent {\n    border-style: none\n}\n\n.hanko_otpCreationDetails {\n    font-weight: var(--font-weight, 400);\n    font-size: var(--font-size, 16px);\n    font-family: var(--font-family, sans-serif);\n    line-height: var(--line-height, 1.4rem);\n    color: var(--color, #333333);\n    margin: var(--item-margin, 0.5rem 0);\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    flex-direction: column;\n    font-size: smaller\n}\n"
  },
  {
    "path": "frontend/elements/nginx/default.conf",
    "content": "server {\n  listen       80;\n  server_name  localhost;\n\n  server_tokens off;\n\n  add_header Access-Control-Allow-Origin *;\n\n  location / {\n    root   /usr/share/nginx/html;\n#     index  index.html;\n    try_files $uri $uri/ @index;\n  }\n\n  location @index {\n    # add_header Cache-Control no-cache;\n    server_tokens off;\n    expires -1;\n    root   /usr/share/nginx/html;\n    try_files /index.html =404;\n  }\n\n  # redirect server error pages to the static page /50x.html\n  #\n  error_page   500 502 503 504  /50x.html;\n  location = /50x.html {\n    root   /usr/share/nginx/html;\n  }\n}\n"
  },
  {
    "path": "frontend/elements/package.json",
    "content": "{\n  \"name\": \"@teamhanko/hanko-elements\",\n  \"version\": \"2.5.0\",\n  \"type\": \"module\",\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"types\": \"dist/index.d.ts\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"browser\": {\n    \".\": \"./dist/elements.js\"\n  },\n  \"typesVersions\": {\n    \"*\": {\n      \"elements\": [\n        \"dist/Elements.d.ts\"\n      ],\n      \"i18n/de\": [\n        \"dist/i18n/de.d.ts\"\n      ],\n      \"i18n/en\": [\n        \"dist/i18n/en.d.ts\"\n      ],\n      \"i18n/fr\": [\n        \"dist/i18n/fr.d.ts\"\n      ],\n      \"i18n/it\": [\n        \"dist/i18n/it.d.ts\"\n      ],\n      \"i18n/nl\": [\n        \"dist/i18n/nl.d.ts\"\n      ],\n      \"i18n/pt-BR\": [\n        \"dist/i18n/pt-BR.d.ts\"\n      ],\n      \"i18n/zh\": [\n        \"dist/i18n/zh.d.ts\"\n      ],\n      \"i18n/bn\":[\n        \"dist/i18n/bn.d.ts\"\n      ],\n      \"i18n/all\": [\n        \"dist/i18n/all.d.ts\"\n      ]\n    }\n  },\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/elements.js\",\n      \"require\": \"./dist/elements.js\"\n    },\n    \"./i18n/de\": {\n      \"types\": \"./dist/i18n/de.d.ts\",\n      \"import\": \"./dist/i18n/de.js\",\n      \"require\": \"./dist/i18n/de.js\"\n    },\n    \"./i18n/en\": {\n      \"types\": \"./dist/i18n/en.d.ts\",\n      \"import\": \"./dist/i18n/en.js\",\n      \"require\": \"./dist/i18n/en.js\"\n    },\n    \"./i18n/fr\": {\n      \"types\": \"./dist/i18n/fr.d.ts\",\n      \"import\": \"./dist/i18n/fr.js\",\n      \"require\": \"./dist/i18n/fr.js\"\n    },\n    \"./i18n/it\": {\n      \"types\": \"./dist/i18n/it.d.ts\",\n      \"import\": \"./dist/i18n/it.js\",\n      \"require\": \"./dist/i18n/it.js\"\n    },\n    \"./i18n/nl\": {\n      \"types\": \"./dist/i18n/nl.d.ts\",\n      \"import\": \"./dist/i18n/nl.js\",\n      \"require\": \"./dist/i18n/nl.js\"\n    },\n    \"./i18n/pt-BR\": {\n      \"types\": \"./dist/i18n/pt-BR.d.ts\",\n      \"import\": \"./dist/i18n/pt-BR.js\",\n      \"require\": \"./dist/i18n/pt-BR.js\"\n    },\n    \"./i18n/zh\": {\n      \"types\": \"./dist/i18n/zh.d.ts\",\n      \"import\": \"./dist/i18n/zh.js\",\n      \"require\": \"./dist/i18n/zh.js\"\n    },\n    \"./i18n/bn\": {\n      \"types\": \"./dist/i18n/bn.d.ts\",\n      \"import\": \"./dist/i18n/bn.js\",\n      \"require\": \"./dist/i18n/bn.js\"\n    },\n    \"./i18n/all\": {\n      \"types\": \"./dist/i18n/all.d.ts\",\n      \"import\": \"./dist/i18n/all.js\",\n      \"require\": \"./dist/i18n/all.js\"\n    }\n  },\n  \"scripts\": {\n    \"lint\": \"eslint 'src/**/*.ts?(x)'\",\n    \"format\": \"pretty-quick --staged\",\n    \"build\": \"npx webpack --mode=production\",\n    \"build:dev\": \"npx webpack --mode=development --config webpack.config.dev.cjs\"\n  },\n  \"description\": \"The <hanko-auth> element offers a complete user interface that will bring a modern login and registration experience to your users.\",\n  \"repository\": \"github:teamhanko/hanko\",\n  \"author\": \"Hanko GmbH <developers@hanko.io>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"hanko\",\n    \"passkey\",\n    \"webauthn\",\n    \"passcode\",\n    \"password\",\n    \"login\",\n    \"register\"\n  ],\n  \"homepage\": \"https://hanko.io\",\n  \"devDependencies\": {\n    \"@typescript-eslint/eslint-plugin\": \"^5.54.0\",\n    \"@typescript-eslint/parser\": \"^5.62.0\",\n    \"css-loader\": \"^7.1.4\",\n    \"eslint\": \"^8.52.0\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"eslint-config-preact\": \"^1.3.0\",\n    \"eslint-config-prettier\": \"^10.1.8\",\n    \"eslint-plugin-prettier\": \"^5.5.5\",\n    \"eslint-plugin-promise\": \"^6.1.1\",\n    \"sass\": \"^1.61.0\",\n    \"sass-loader\": \"^16.0.7\",\n    \"source-map-loader\": \"^4.0.1\",\n    \"style-loader\": \"^3.3.2\",\n    \"ts-jest\": \"^29.1.1\",\n    \"ts-loader\": \"^9.4.2\",\n    \"webpack\": \"^5.105.2\",\n    \"webpack-cli\": \"^7.0.2\"\n  },\n  \"dependencies\": {\n    \"@denysvuika/preact-translate\": \"^0.6.0\",\n    \"@teamhanko/hanko-frontend-sdk\": \"^2.5.0\",\n    \"@teamhanko/preact-custom-element\": \"^4.2.2\",\n    \"classnames\": \"^2.3.2\",\n    \"preact\": \"^10.28.4\"\n  }\n}\n"
  },
  {
    "path": "frontend/elements/src/Elements.tsx",
    "content": "import { JSX, FunctionalComponent } from \"preact\";\nimport registerCustomElement from \"@teamhanko/preact-custom-element\";\nimport AppProvider, {\n  ComponentName,\n  GlobalOptions,\n  HankoAuthMode,\n} from \"./contexts/AppProvider\";\nimport { CookieSameSite, Hanko } from \"@teamhanko/hanko-frontend-sdk\";\nimport { defaultTranslations, Translations } from \"./i18n/translations\";\nimport { SessionTokenLocation } from \"@teamhanko/hanko-frontend-sdk/dist/lib/client/HttpClient\";\n\nexport interface HankoAuthAdditionalProps {\n  prefilledEmail?: string;\n  prefilledUsername?: string;\n  mode?: HankoAuthMode;\n}\n\nexport declare interface HankoAuthElementProps\n  extends JSX.HTMLAttributes<HTMLElement>,\n    HankoAuthAdditionalProps {}\n\nexport declare interface HankoProfileElementProps\n  extends JSX.HTMLAttributes<HTMLElement> {}\n\nexport declare interface HankoEventsElementProps\n  extends JSX.HTMLAttributes<HTMLElement> {}\n\ndeclare global {\n  // eslint-disable-next-line no-unused-vars\n  namespace JSX {\n    // eslint-disable-next-line no-unused-vars\n    interface IntrinsicElements {\n      \"hanko-auth\": HankoAuthElementProps;\n      \"hanko-login\": HankoAuthElementProps;\n      \"hanko-registration\": HankoAuthElementProps;\n      \"hanko-profile\": HankoProfileElementProps;\n      \"hanko-events\": HankoEventsElementProps;\n    }\n  }\n}\n\n// React 19 and later\ndeclare module \"react\" {\n  // eslint-disable-next-line no-unused-vars\n  namespace JSX {\n    // eslint-disable-next-line no-unused-vars\n    interface IntrinsicElements {\n      \"hanko-auth\": HankoAuthElementProps;\n      \"hanko-login\": HankoAuthElementProps;\n      \"hanko-registration\": HankoAuthElementProps;\n      \"hanko-profile\": HankoProfileElementProps;\n      \"hanko-events\": HankoEventsElementProps;\n    }\n  }\n}\n\nexport interface RegisterOptions {\n  shadow?: boolean;\n  injectStyles?: boolean;\n  enablePasskeys?: boolean;\n  hidePasskeyButtonOnLogin?: boolean;\n  translations?: Translations;\n  translationsLocation?: string;\n  fallbackLanguage?: string;\n  storageKey?: string;\n  cookieDomain?: string;\n  cookieSameSite?: CookieSameSite;\n  sessionCheckInterval?: number;\n  sessionTokenLocation?: SessionTokenLocation;\n}\n\nexport interface RegisterResult {\n  hanko: Hanko;\n}\n\ninterface InternalRegisterOptions extends RegisterOptions {\n  tagName: string;\n  entryComponent: FunctionalComponent<HankoAuthAdditionalProps>;\n  observedAttributes: string[];\n}\n\nconst globalOptions: GlobalOptions = {};\n\nconst createHankoComponent = (\n  componentName: ComponentName,\n  props: Record<string, any>,\n) => {\n  // In most browser the IDL property (script['nonce']) is the only way to access nonces.\n  // Because of that the nonce would be an empty string in the props property.\n  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/nonce\n  const nonce = document\n    .getElementsByTagName(`hanko-${componentName}`)\n    // @ts-ignore\n    .item(0)?.nonce;\n  return (\n    <AppProvider\n      componentName={componentName}\n      globalOptions={globalOptions}\n      createWebauthnAbortSignal={createWebauthnAbortSignal}\n      {...props}\n      nonce={nonce}\n    />\n  );\n};\n\nconst HankoAuth = (props: HankoAuthElementProps) => {\n  return createHankoComponent(\"auth\", props);\n};\n\nconst HankoLogin = (props: HankoAuthElementProps) =>\n  createHankoComponent(\"login\", props);\n\nconst HankoRegistration = (props: HankoProfileElementProps) =>\n  createHankoComponent(\"registration\", props);\n\nconst HankoProfile = (props: HankoProfileElementProps) =>\n  createHankoComponent(\"profile\", props);\n\nconst HankoEvents = (props: HankoEventsElementProps) =>\n  createHankoComponent(\"events\", props);\n\nlet webauthnAbortController = new AbortController();\n\nconst createWebauthnAbortSignal = () => {\n  if (webauthnAbortController) {\n    webauthnAbortController.abort();\n  }\n\n  webauthnAbortController = new AbortController();\n  return webauthnAbortController.signal;\n};\n\nconst _register = async ({\n  tagName,\n  entryComponent,\n  shadow = true,\n  observedAttributes,\n}: InternalRegisterOptions) => {\n  if (!customElements.get(tagName)) {\n    registerCustomElement(entryComponent, tagName, observedAttributes, {\n      shadow,\n    });\n  }\n};\n\nexport const register = async (\n  api: string,\n  options: RegisterOptions = {},\n): Promise<RegisterResult> => {\n  const observedAttributes = [\n    \"api\",\n    \"lang\",\n    \"prefilled-email\",\n    \"entry\",\n    \"mode\",\n  ];\n\n  options = {\n    shadow: true,\n    injectStyles: true,\n    enablePasskeys: true,\n    hidePasskeyButtonOnLogin: false,\n    translations: null,\n    translationsLocation: \"/i18n\",\n    fallbackLanguage: \"en\",\n    storageKey: \"hanko\",\n    sessionCheckInterval: 30000,\n    ...options,\n  };\n\n  globalOptions.hanko = new Hanko(api, {\n    cookieName: options.storageKey,\n    cookieDomain: options.cookieDomain,\n    cookieSameSite: options.cookieSameSite,\n    localStorageKey: options.storageKey,\n    sessionCheckInterval: options.sessionCheckInterval,\n    sessionTokenLocation: options.sessionTokenLocation,\n  });\n  globalOptions.injectStyles = options.injectStyles;\n  globalOptions.enablePasskeys = options.enablePasskeys;\n  globalOptions.hidePasskeyButtonOnLogin = options.hidePasskeyButtonOnLogin;\n  globalOptions.translations = options.translations || defaultTranslations;\n  globalOptions.translationsLocation = options.translationsLocation;\n  globalOptions.fallbackLanguage = options.fallbackLanguage;\n  globalOptions.storageKey = options.storageKey;\n  await Promise.all([\n    _register({\n      ...options,\n      tagName: \"hanko-auth\",\n      entryComponent: HankoAuth,\n      observedAttributes,\n    }),\n    _register({\n      ...options,\n      tagName: \"hanko-login\",\n      entryComponent: HankoLogin,\n      observedAttributes,\n    }),\n    _register({\n      ...options,\n      tagName: \"hanko-registration\",\n      entryComponent: HankoRegistration,\n      observedAttributes,\n    }),\n    _register({\n      ...options,\n      tagName: \"hanko-profile\",\n      entryComponent: HankoProfile,\n      observedAttributes: observedAttributes.filter((attribute) =>\n        [\"api\", \"lang\"].includes(attribute),\n      ),\n    }),\n    _register({\n      ...options,\n      tagName: \"hanko-events\",\n      entryComponent: HankoEvents,\n      observedAttributes: [],\n    }),\n  ]);\n\n  return { hanko: globalOptions.hanko };\n};\n"
  },
  {
    "path": "frontend/elements/src/_mixins.sass",
    "content": "@use 'variables'\n\n@mixin font\n  font-weight: variables.$font-weight\n  font-size: variables.$font-size\n  font-family: variables.$font-family\n  line-height: variables.$line-height\n\n@mixin border\n  border-radius: variables.$border-radius\n  border-style: variables.$border-style\n  border-width: variables.$border-width\n\n\n"
  },
  {
    "path": "frontend/elements/src/_preset.sass",
    "content": "// Color Scheme\n$color: #333333\n$color-shade-1: #8f9095\n$color-shade-2: #e5e6ef\n\n$brand-color: #506cf0\n$brand-color-shade-1: #6b84fb\n$brand-contrast-color: white\n\n$background-color: white\n$error-color: #e82020\n$link-color: #506cf0\n\n// Font Styles\n$font-weight: 400\n$font-size: 16px\n$font-family: sans-serif\n$line-height: 1.4rem\n\n// Border Styles\n$border-radius: 8px\n$border-style: solid\n$border-width: 1px\n\n// Item Styles\n$item-height: 42px\n$item-margin: .5rem 0\n\n// Container Styles\n$container-padding: 30px\n$container-max-width: 410px\n\n// Headline Styles\n$headline1-font-size: 24px\n$headline1-font-weight: 600\n$headline1-margin: 0 0 .5rem\n\n$headline2-font-size: 16px\n$headline2-font-weight: 600\n$headline2-margin: 1rem 0 .5rem\n\n// Divider Styles\n$divider-padding: 0 42px\n$divider-visibility: visible\n\n// Link Styles\n$link-text-decoration: none\n$link-text-decoration-hover: underline\n\n// Input Styles\n$input-min-width: 14em\n\n// Button Styles\n$button-min-width: 7em\n"
  },
  {
    "path": "frontend/elements/src/_variables.sass",
    "content": "@use 'preset'\n\n// Color Scheme\n$color: var(--color, preset.$color)\n$color-shade-1: var(--color-shade-1, preset.$color-shade-1)\n$color-shade-2: var(--color-shade-2, preset.$color-shade-2)\n\n$brand-color: var(--brand-color, preset.$brand-color)\n$brand-color-shade-1: var(--brand-color-shade-1, preset.$brand-color-shade-1)\n$brand-contrast-color: var(--brand-contrast-color, preset.$brand-contrast-color)\n\n$background-color: var(--background-color, preset.$background-color)\n$error-color: var(--error-color, preset.$error-color)\n$link-color: var(--link-color, preset.$link-color)\n\n// Font Styles\n$font-weight: var(--font-weight, preset.$font-weight)\n$font-size: var(--font-size, preset.$font-size)\n$font-family: var(--font-family, preset.$font-family)\n$line-height: var(--line-height, preset.$line-height)\n\n// Border Styles\n$border-radius: var(--border-radius, preset.$border-radius)\n$border-style: var(--border-style, preset.$border-style)\n$border-width: var(--border-width, preset.$border-width)\n\n// Item Styles\n$item-height: var(--item-height, preset.$item-height)\n$item-margin: var(--item-margin, preset.$item-margin)\n\n// Container Styles\n$container-padding: var(--container-padding, preset.$container-padding)\n$container-max-width: var(--container-max-width, preset.$container-max-width)\n\n// Headline Styles\n$headline1-font-weight: var(--headline1-font-weight, preset.$headline1-font-weight)\n$headline1-font-size: var(--headline1-font-size, preset.$headline1-font-size)\n$headline1-margin: var(--headline1-margin, preset.$headline1-margin)\n\n$headline2-font-weight: var(--headline2-font-weight, preset.$headline2-font-weight)\n$headline2-font-size: var(--headline2-font-size, preset.$headline2-font-size)\n$headline2-margin: var(--headline2-margin, preset.$headline2-margin)\n\n// Divider Styles\n$divider-padding: var(--divider-padding, preset.$divider-padding)\n$divider-visibility: var(--divider-visibility, preset.$divider-visibility)\n\n// Link Styles\n$link-text-decoration: var(--link-text-decoration, preset.$link-text-decoration)\n$link-text-decoration-hover: var(--link-text-decoration-hover, preset.$link-text-decoration-hover)\n\n// Input Styles\n$input-min-width: var(--input-min-width, preset.$input-min-width)\n\n// Button Styles\n$button-min-width: var(--button-min-width, preset.$button-min-width)\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/Accordion.tsx",
    "content": "import { h } from \"preact\";\nimport { Dispatch, SetStateAction, useCallback } from \"preact/compat\";\ntype Selector<T> = (item: T, itemIndex?: number) => string | h.JSX.Element;\n\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\ninterface Props<T> {\n  name: string;\n  columnSelector: Selector<T>;\n  contentSelector: Selector<T>;\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string | null>>;\n  data: Array<T>;\n  dropdown?: boolean;\n}\n\nconst Accordion = function <T>({\n  name,\n  columnSelector,\n  contentSelector,\n  data = [],\n  checkedItemID,\n  setCheckedItemID,\n  dropdown = false,\n}: Props<T>) {\n  const toID = useCallback(\n    (itemIndex: number) => `${name}-${itemIndex}`,\n    [name],\n  );\n\n  const checked = useCallback(\n    (itemIndex: number) => toID(itemIndex) === checkedItemID,\n    [checkedItemID, toID],\n  );\n\n  const clickHandler = (event: Event) => {\n    if (!(event.target instanceof HTMLInputElement)) return;\n    const itemIndex = parseInt(event.target.value, 10);\n    const id = toID(itemIndex);\n    setCheckedItemID(id === checkedItemID ? null : id);\n  };\n\n  return (\n    <div className={styles.accordion}>\n      {data.map((item, itemIndex) => (\n        <div className={styles.accordionItem} key={itemIndex}>\n          <input\n            type={\"radio\"}\n            className={styles.accordionInput}\n            id={`${name}-${itemIndex}`}\n            name={name}\n            onClick={clickHandler}\n            value={itemIndex}\n            checked={checked(itemIndex)}\n          />\n          <label\n            className={cx(styles.label, dropdown && styles.dropdown)}\n            for={`${name}-${itemIndex}`}\n          >\n            <span className={styles.labelText}>\n              {columnSelector(item, itemIndex)}\n            </span>\n          </label>\n          <div\n            className={cx(\n              styles.accordionContent,\n              dropdown && styles.dropdownContent,\n            )}\n          >\n            {contentSelector(item, itemIndex)}\n          </div>\n        </div>\n      ))}\n    </div>\n  );\n};\n\nexport default Accordion;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/AddEmailDropdown.tsx",
    "content": "import { h } from \"preact\";\nimport { Dispatch, SetStateAction, useContext, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Form from \"../form/Form\";\nimport Input from \"../form/Input\";\nimport Button from \"../form/Button\";\nimport Dropdown from \"./Dropdown\";\nimport ErrorMessage from \"../error/ErrorMessage\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\nimport { AppContext } from \"../../contexts/AppProvider\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst AddEmailDropdown = ({\n  checkedItemID,\n  setCheckedItemID,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { setUIState } = useContext(AppContext);\n  const [newEmail, setNewEmail] = useState<string>();\n\n  const onInputHandler = (event: Event) => {\n    event.preventDefault();\n    if (event.target instanceof HTMLInputElement) {\n      setNewEmail(event.target.value);\n    }\n  };\n\n  const onEmailSubmit = async (event: Event, email: string) => {\n    event.preventDefault();\n    setUIState((prev) => ({ ...prev, email }));\n    const nextState = await flowState.actions.email_create.run(\n      { email },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  return (\n    <Dropdown\n      name={\"email-create-dropdown\"}\n      title={t(\"labels.addEmail\")}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    >\n      <ErrorMessage\n        flowError={flowState.actions.email_create.inputs.email?.error}\n      />\n      <Form\n        flowAction={flowState.actions.email_create}\n        onSubmit={(event: Event) =>\n          onEmailSubmit(event, newEmail).then(() => setNewEmail(\"\"))\n        }\n      >\n        <Input\n          markError\n          type={\"email\"}\n          placeholder={t(\"labels.newEmailAddress\")}\n          onInput={onInputHandler}\n          value={newEmail}\n          flowInput={flowState.actions.email_create.inputs.email}\n        />\n        <Button>{t(\"labels.save\")}</Button>\n      </Form>\n    </Dropdown>\n  );\n};\n\nexport default AddEmailDropdown;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/AddWebauthnCredentialDropdown.tsx",
    "content": "import { Dispatch, SetStateAction, useContext } from \"preact/compat\";\n\nimport { WebauthnSupport, State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Form from \"../form/Form\";\nimport Button from \"../form/Button\";\nimport Paragraph from \"../paragraph/Paragraph\";\nimport Dropdown from \"./Dropdown\";\n\ntype CredentialType = \"passkey\" | \"security-key\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  credentialType: CredentialType;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst AddWebauthnCredentialDropdown = ({\n  checkedItemID,\n  setCheckedItemID,\n  credentialType,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n\n  const webauthnSupported = WebauthnSupport.supported();\n\n  const action =\n    credentialType == \"passkey\"\n      ? flowState.actions.webauthn_credential_create\n      : flowState.actions.security_key_create;\n  const onSubmit = async (event: Event) => {\n    event.preventDefault();\n\n    const nextState = await action.run(null, {\n      dispatchAfterStateChangeEvent: false,\n    });\n    return onState(nextState);\n  };\n\n  return (\n    <Dropdown\n      name={\n        credentialType === \"security-key\"\n          ? \"security-key-create-dropdown\"\n          : \"passkey-create-dropdown\"\n      }\n      title={\n        credentialType === \"security-key\"\n          ? t(\"labels.createSecurityKey\")\n          : t(\"labels.createPasskey\")\n      }\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    >\n      <Paragraph>\n        {credentialType === \"security-key\"\n          ? t(\"texts.securityKeySetUp\")\n          : t(\"texts.setupPasskey\")}\n      </Paragraph>\n      <Form onSubmit={onSubmit} flowAction={action}>\n        <Button\n          title={!webauthnSupported ? t(\"labels.webauthnUnsupported\") : null}\n        >\n          {credentialType === \"security-key\"\n            ? t(\"labels.createSecurityKey\")\n            : t(\"labels.createPasskey\")}\n        </Button>\n      </Form>\n    </Dropdown>\n  );\n};\n\nexport default AddWebauthnCredentialDropdown;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ChangePasswordDropdown.tsx",
    "content": "import { h } from \"preact\";\nimport { Dispatch, SetStateAction, useContext, useState } from \"preact/compat\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Form from \"../form/Form\";\nimport Input from \"../form/Input\";\nimport Button from \"../form/Button\";\nimport Paragraph from \"../paragraph/Paragraph\";\nimport Dropdown from \"./Dropdown\";\nimport Link from \"../link/Link\";\nimport ErrorMessage from \"../error/ErrorMessage\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst ChangePasswordDropdown = ({\n  checkedItemID,\n  setCheckedItemID,\n  onState,\n  flowState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const [newPassword, setNewPassword] = useState<string>(\"\");\n\n  const action = flowState.actions.password_create.enabled\n    ? flowState.actions.password_create\n    : flowState.actions.password_update;\n\n  const onInputHandler = (event: Event) => {\n    event.preventDefault();\n    if (event.target instanceof HTMLInputElement) {\n      setNewPassword(event.target.value);\n    }\n  };\n\n  const onPasswordSubmit = async (event: Event, password: string) => {\n    event.preventDefault();\n    const nextState = await action.run(\n      { password },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const onPasswordDelete = async (event: Event) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.password_delete.run(null, {\n      dispatchAfterStateChangeEvent: false,\n    });\n    return onState(nextState);\n  };\n\n  return (\n    <Dropdown\n      name={\"password-edit-dropdown\"}\n      title={t(\n        flowState.actions.password_create.enabled\n          ? \"labels.setPassword\"\n          : \"labels.changePassword\",\n      )}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    >\n      <Paragraph>\n        {t(\"texts.passwordFormatHint\", {\n          minLength: action.inputs.password.min_length?.toString(10),\n          maxLength: action.inputs.password.max_length?.toString(10),\n        })}\n      </Paragraph>\n      <ErrorMessage\n        flowError={flowState.actions.password_create.inputs.password?.error}\n      />\n      <Form\n        flowAction={action}\n        onSubmit={(event: Event) =>\n          onPasswordSubmit(event, newPassword).then(() => setNewPassword(\"\"))\n        }\n      >\n        <Input\n          markError\n          autoComplete={\"new-password\"}\n          placeholder={t(\"labels.newPassword\")}\n          type={\"password\"}\n          onInput={onInputHandler}\n          value={newPassword}\n          flowInput={action.inputs.password}\n        />\n        <Button>{t(\"labels.save\")}</Button>\n      </Form>\n      <Link\n        dangerous\n        flowAction={flowState.actions.password_delete}\n        onClick={(event: Event) =>\n          onPasswordDelete(event).then(() => setNewPassword(\"\"))\n        }\n        loadingSpinnerPosition={\"right\"}\n      >\n        {t(\"labels.delete\")}\n      </Link>\n    </Dropdown>\n  );\n};\n\nexport default ChangePasswordDropdown;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ChangeUsernameDropdown.tsx",
    "content": "import { h } from \"preact\";\nimport { Dispatch, SetStateAction, useContext, useState } from \"preact/compat\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Form from \"../form/Form\";\nimport Input from \"../form/Input\";\nimport Button from \"../form/Button\";\nimport Dropdown from \"./Dropdown\";\nimport ErrorMessage from \"../error/ErrorMessage\";\nimport Link from \"../link/Link\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst ChangeUsernameDropdown = ({\n  checkedItemID,\n  setCheckedItemID,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const [username, setUsername] = useState<string>();\n\n  const onInputHandler = (event: Event) => {\n    event.preventDefault();\n    if (event.target instanceof HTMLInputElement) {\n      setUsername(event.target.value);\n    }\n  };\n\n  const onSubmit = async (event: Event) => {\n    event.preventDefault();\n    const action = flowState.payload.user.username\n      ? flowState.actions.username_update\n      : flowState.actions.username_create;\n    const nextState = await action.run(\n      { username },\n      { dispatchAfterStateChangeEvent: false },\n    );\n\n    return onState(nextState).then(() => setUsername(\"\"));\n  };\n\n  const onDelete = async (event: Event) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.username_delete.run(null, {\n      dispatchAfterStateChangeEvent: false,\n    });\n    return onState(nextState).then(() => setUsername(\"\"));\n  };\n\n  return (\n    <Dropdown\n      name={\"username-edit-dropdown\"}\n      title={t(\n        flowState.payload.user.username\n          ? \"labels.changeUsername\"\n          : \"labels.setUsername\",\n      )}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    >\n      <ErrorMessage\n        flowError={\n          flowState.payload.user.username\n            ? flowState.actions.username_update.inputs.username?.error\n            : flowState.actions.username_create.inputs.username?.error\n        }\n      />\n      <Form\n        flowAction={\n          flowState.payload.user.username\n            ? flowState.actions.username_update\n            : flowState.actions.username_create\n        }\n        onSubmit={onSubmit}\n      >\n        <Input\n          markError\n          placeholder={t(\"labels.username\")}\n          type={\"text\"}\n          onInput={onInputHandler}\n          value={username}\n          flowInput={\n            flowState.payload.user.username\n              ? flowState.actions.username_update.inputs.username\n              : flowState.actions.username_create.inputs.username\n          }\n        />\n        <Button>{t(\"labels.save\")}</Button>\n      </Form>\n      <Link\n        flowAction={flowState.actions.username_delete}\n        onClick={onDelete}\n        dangerous\n        loadingSpinnerPosition={\"right\"}\n      >\n        {t(\"labels.delete\")}\n      </Link>\n    </Dropdown>\n  );\n};\n\nexport default ChangeUsernameDropdown;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ConnectIdentityDropdown.tsx",
    "content": "import { Dispatch, SetStateAction, useContext } from \"preact/compat\";\nimport {\n  clearStoredCodeVerifier,\n  generateCodeVerifier,\n  setStoredCodeVerifier,\n  State,\n} from \"@teamhanko/hanko-frontend-sdk\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport Dropdown from \"./Dropdown\";\nimport ErrorMessage from \"../error/ErrorMessage\";\nimport Button from \"../form/Button\";\nimport Form from \"../form/Form\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n\n  onState(state: State<any>): Promise<void>;\n}\n\nconst ConnectIdentityDropdown = ({\n  checkedItemID,\n  setCheckedItemID,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n\n  const onSubmit = async (event: Event, provider: string) => {\n    event.preventDefault();\n\n    const codeVerifier = generateCodeVerifier();\n    setStoredCodeVerifier(codeVerifier);\n\n    try {\n      const nextState =\n        await flowState.actions.connect_thirdparty_oauth_provider.run({\n          provider,\n          redirect_to: window.location.href,\n          code_verifier: codeVerifier,\n        });\n\n      if (nextState.error) {\n        clearStoredCodeVerifier();\n      }\n\n      return onState(nextState);\n    } catch (e) {\n      clearStoredCodeVerifier();\n      throw e;\n    }\n  };\n\n  return (\n    <Dropdown\n      name={\"connect-account-dropdown\"}\n      title={t(\"labels.connectAccount\")}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    >\n      <ErrorMessage\n        flowError={\n          flowState.actions.connect_thirdparty_oauth_provider.inputs.provider\n            ?.error\n        }\n      />\n\n      {flowState.actions.connect_thirdparty_oauth_provider.inputs.provider.allowed_values?.map(\n        (provider) => {\n          return (\n            <Form\n              key={provider.value}\n              flowAction={flowState.actions.connect_thirdparty_oauth_provider}\n              onSubmit={(event) => onSubmit(event, provider.value)}\n            >\n              <Button\n                key={provider}\n                // @ts-ignore\n                icon={\n                  provider.value.startsWith(\"custom_\")\n                    ? \"customProvider\"\n                    : provider.value\n                }\n              >\n                {provider.name}\n              </Button>\n            </Form>\n          );\n        },\n      )}\n    </Dropdown>\n  );\n};\n\nexport default ConnectIdentityDropdown;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/Dropdown.tsx",
    "content": "import { ComponentChildren, Fragment, h } from \"preact\";\nimport { Dispatch, SetStateAction } from \"preact/compat\";\n\nimport Accordion from \"./Accordion\";\n\ninterface Props {\n  name: string;\n  title: string | h.JSX.Element;\n  children: ComponentChildren;\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n}\n\nconst Dropdown = ({\n  name,\n  title,\n  children,\n  checkedItemID,\n  setCheckedItemID,\n}: Props) => {\n  return (\n    <Accordion\n      dropdown\n      name={name}\n      columnSelector={() => title}\n      contentSelector={() => <>{children}</>}\n      setCheckedItemID={setCheckedItemID}\n      checkedItemID={checkedItemID}\n      data={[{}]}\n    />\n  );\n};\n\nexport default Dropdown;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ListEmailsAccordion.tsx",
    "content": "import { Fragment } from \"preact\";\nimport { Dispatch, SetStateAction, useContext, useMemo } from \"preact/compat\";\nimport { State, Email } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport styles from \"./styles.sass\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Accordion from \"./Accordion\";\nimport Paragraph from \"../paragraph/Paragraph\";\nimport Headline2 from \"../headline/Headline2\";\nimport Link from \"../link/Link\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst ListEmailsAccordion = ({\n  checkedItemID,\n  setCheckedItemID,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const isDisabled = useMemo(() => false, []);\n\n  const onEmailDelete = async (event: Event, emailID: string) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.email_delete.run(\n      {\n        email_id: emailID,\n      },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const onEmailSetPrimary = async (event: Event, emailID: string) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.email_set_primary.run(\n      {\n        email_id: emailID,\n      },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const onEmailVerify = async (event: Event, emailID: string) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.email_verify.run(\n      {\n        email_id: emailID,\n      },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const labels = (email: Email) => {\n    const description = (\n      <span className={styles.description}>\n        {!email.is_verified ? (\n          <>\n            {\" -\"} {t(\"labels.unverifiedEmail\")}\n          </>\n        ) : email.is_primary ? (\n          <>\n            {\" -\"} {t(\"labels.primaryEmail\")}\n          </>\n        ) : null}\n      </span>\n    );\n\n    return email.is_primary ? (\n      <>\n        <b>{email.address}</b>\n        {description}\n      </>\n    ) : (\n      <>\n        {email.address}\n        {description}\n      </>\n    );\n  };\n\n  const contents = (email: Email) => (\n    <>\n      {!email.is_primary ? (\n        <>\n          <Paragraph>\n            <Headline2>{t(\"headlines.setPrimaryEmail\")}</Headline2>\n            {t(\"texts.setPrimaryEmail\")}\n            <br />\n            <Link\n              flowAction={flowState.actions.email_set_primary}\n              onClick={(event: Event) => onEmailSetPrimary(event, email.id)}\n              loadingSpinnerPosition={\"right\"}\n            >\n              {t(\"labels.setAsPrimaryEmail\")}\n            </Link>\n          </Paragraph>\n        </>\n      ) : (\n        <>\n          <Paragraph>\n            <Headline2>{t(\"headlines.isPrimaryEmail\")}</Headline2>\n            {t(\"texts.isPrimaryEmail\")}\n          </Paragraph>\n        </>\n      )}\n      {email.is_verified ? (\n        <>\n          <Paragraph>\n            <Headline2>{t(\"headlines.emailVerified\")}</Headline2>\n            {t(\"texts.emailVerified\")}\n          </Paragraph>\n        </>\n      ) : (\n        <>\n          <Paragraph>\n            <Headline2>{t(\"headlines.emailUnverified\")}</Headline2>\n            {t(\"texts.emailUnverified\")}\n            <br />\n            <Link\n              flowAction={flowState.actions.email_verify}\n              onClick={(event) => onEmailVerify(event, email.id)}\n              loadingSpinnerPosition={\"right\"}\n            >\n              {t(\"labels.verify\")}\n            </Link>\n          </Paragraph>\n        </>\n      )}\n      {flowState.actions.email_delete.inputs.email_id.allowed_values\n        ?.map((e) => e.value)\n        .includes(email.id) ? (\n        <>\n          <Paragraph>\n            <Headline2>{t(\"headlines.emailDelete\")}</Headline2>\n            {t(\"texts.emailDelete\")}\n            <br />\n            <Link\n              dangerous\n              flowAction={flowState.actions.email_delete}\n              onClick={(event) => onEmailDelete(event, email.id)}\n              disabled={isDisabled}\n              loadingSpinnerPosition={\"right\"}\n            >\n              {t(\"labels.delete\")}\n            </Link>\n          </Paragraph>\n        </>\n      ) : null}\n      {email.identities?.length > 0 ? (\n        <>\n          <Paragraph>\n            <Headline2>{t(\"headlines.connectedAccounts\")}</Headline2>\n            {email.identities.map((i) => i.provider).join(\", \")}\n          </Paragraph>\n        </>\n      ) : null}\n    </>\n  );\n  return (\n    <Accordion\n      name={\"email-edit-dropdown\"}\n      columnSelector={labels}\n      data={flowState.payload.user.emails}\n      contentSelector={contents}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    />\n  );\n};\n\nexport default ListEmailsAccordion;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ListIdentities.tsx",
    "content": "import { Identity, State } from \"@teamhanko/hanko-frontend-sdk\";\nimport { Dispatch, SetStateAction, useContext, useMemo } from \"preact/compat\";\nimport { Fragment } from \"preact\";\nimport Accordion from \"./Accordion\";\nimport Paragraph from \"../paragraph/Paragraph\";\nimport Headline2 from \"../headline/Headline2\";\nimport Link from \"../link/Link\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst ListIdentities = ({\n  checkedItemID,\n  setCheckedItemID,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const isDisabled = useMemo(() => false, []);\n\n  const labels = (identity: Identity) => {\n    const headline = <b>{identity.provider}</b>;\n    return <>{headline}</>;\n  };\n\n  const onIdentityDelete = async (event: Event, identityID: string) => {\n    event.preventDefault();\n    const nextState =\n      await flowState.actions.disconnect_thirdparty_oauth_provider.run(\n        {\n          identity_id: identityID,\n        },\n        { dispatchAfterStateChangeEvent: false },\n      );\n    return onState(nextState);\n  };\n\n  const contents = (identity: Identity) => (\n    <>\n      <>\n        <Paragraph>\n          <Headline2>{t(\"headlines.deleteIdentity\")}</Headline2>\n          <Link\n            dangerous\n            flowAction={flowState.actions.disconnect_thirdparty_oauth_provider}\n            onClick={(event) => onIdentityDelete(event, identity.identity_id)}\n            disabled={isDisabled}\n            loadingSpinnerPosition={\"right\"}\n          >\n            {t(\"labels.delete\")}\n          </Link>\n        </Paragraph>\n      </>\n    </>\n  );\n\n  return (\n    <Accordion\n      name={\"connected-accounts\"}\n      columnSelector={labels}\n      contentSelector={contents}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n      data={flowState.payload.user.identities}\n    />\n  );\n};\n\nexport default ListIdentities;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ListSessionsAccordion.tsx",
    "content": "import { Dispatch, SetStateAction, useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State, Session } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Accordion from \"./Accordion\";\nimport { Fragment } from \"preact\";\nimport Paragraph from \"../paragraph/Paragraph\";\nimport Headline2 from \"../headline/Headline2\";\nimport Link from \"../link/Link\";\nimport styles from \"./styles.sass\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst ListSessionsAccordion = ({\n  checkedItemID,\n  setCheckedItemID,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n\n  const onSessionDelete = async (event: Event, id: string) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.session_delete.run(\n      {\n        session_id: id,\n      },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const labels = (session: Session) => {\n    const headline = (\n      <b>{session.user_agent ? session.user_agent : session.id}</b>\n    );\n    const description = session.current ? (\n      <span className={styles.description}>\n        <>\n          {\" -\"} {t(\"labels.currentSession\")}\n        </>\n      </span>\n    ) : null;\n    return (\n      <>\n        {headline}\n        {description}\n      </>\n    );\n  };\n\n  const convertTime = (t: string) => new Date(t).toLocaleString();\n\n  const contents = (session: Session) => (\n    <>\n      <Paragraph hidden={!session.ip_address}>\n        <Headline2>{t(\"headlines.ipAddress\")}</Headline2>\n        {session.ip_address}\n      </Paragraph>\n      <Paragraph>\n        <Headline2>{t(\"headlines.lastUsed\")}</Headline2>\n        {convertTime(session.last_used)}\n      </Paragraph>\n      <Paragraph>\n        <Headline2>{t(\"headlines.createdAt\")}</Headline2>\n        {convertTime(session.created_at)}\n      </Paragraph>\n      {flowState.actions.session_delete.inputs.session_id?.allowed_values\n        ?.map((e) => e.value)\n        ?.includes(session.id) ? (\n        <Paragraph>\n          <Headline2>{t(\"headlines.revokeSession\")}</Headline2>\n          <Link\n            dangerous\n            onClick={(event) => onSessionDelete(event, session.id)}\n            loadingSpinnerPosition={\"right\"}\n          >\n            {t(\"labels.revoke\")}\n          </Link>\n        </Paragraph>\n      ) : null}\n    </>\n  );\n\n  return (\n    <Accordion\n      name={\"session-edit-dropdown\"}\n      columnSelector={labels}\n      data={flowState.payload.sessions}\n      contentSelector={contents}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    />\n  );\n};\n\nexport default ListSessionsAccordion;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ListWebauthnCredentialsAccordion.tsx",
    "content": "import { Fragment } from \"preact\";\nimport { Dispatch, SetStateAction, useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State, WebauthnCredential } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Accordion from \"./Accordion\";\nimport Paragraph from \"../paragraph/Paragraph\";\nimport Link from \"../link/Link\";\nimport Headline2 from \"../headline/Headline2\";\nimport { AppContext } from \"../../contexts/AppProvider\";\nimport RenameWebauthnCredentialPage from \"../../pages/RenameWebauthnCredentialPage\";\n\ntype CredentialType = \"passkey\" | \"security-key\";\n\ninterface Props {\n  credentials: WebauthnCredential[];\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  onBack: (event: Event) => Promise<void>;\n  onCredentialNameSubmit: (\n    event: Event,\n    id: string,\n    name: string,\n  ) => Promise<void>;\n  onCredentialDelete: (event: Event, id: string) => Promise<void>;\n  allowCredentialDeletion?: boolean;\n  credentialType: CredentialType;\n  flowState: State<\"profile_init\">;\n}\n\nconst ListWebauthnCredentialsAccordion = ({\n  credentials = [],\n  checkedItemID,\n  setCheckedItemID,\n  onBack,\n  onCredentialNameSubmit,\n  allowCredentialDeletion,\n  credentialType,\n  onCredentialDelete,\n  flowState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { setPage } = useContext(AppContext);\n\n  const renameCredential = (\n    event: Event,\n    credential: WebauthnCredential,\n    credentialType: CredentialType,\n  ) => {\n    event.preventDefault();\n    setPage(\n      <RenameWebauthnCredentialPage\n        oldName={uiDisplayName(credential)}\n        credential={credential}\n        credentialType={credentialType}\n        onBack={onBack}\n        onCredentialNameSubmit={onCredentialNameSubmit}\n        flowState={flowState}\n      />,\n    );\n  };\n\n  const uiDisplayName = (credential: WebauthnCredential) => {\n    if (credential.name) {\n      return credential.name;\n    }\n    const alphanumeric = credential.public_key.replace(/[\\W_]/g, \"\");\n    return `${\n      credentialType === \"security-key\" ? \"SecurityKey\" : \"Passkey\"\n    }-${alphanumeric.substring(alphanumeric.length - 7, alphanumeric.length)}`;\n  };\n\n  const convertTime = (t: string) => new Date(t).toLocaleString();\n\n  const labels = (credential: WebauthnCredential) => uiDisplayName(credential);\n\n  const contents = (credential: WebauthnCredential) => (\n    <>\n      <Paragraph>\n        <Headline2>\n          {credentialType === \"security-key\"\n            ? t(\"headlines.renameSecurityKey\")\n            : t(\"headlines.renamePasskey\")}\n        </Headline2>\n        {credentialType === \"security-key\"\n          ? t(\"texts.renameSecurityKey\")\n          : t(\"texts.renamePasskey\")}\n        <br />\n        <Link\n          onClick={(event) =>\n            renameCredential(event, credential, credentialType)\n          }\n          loadingSpinnerPosition={\"right\"}\n        >\n          {t(\"labels.rename\")}\n        </Link>\n      </Paragraph>\n      <Paragraph hidden={!allowCredentialDeletion}>\n        <Headline2>\n          {credentialType === \"security-key\"\n            ? t(\"headlines.deleteSecurityKey\")\n            : t(\"headlines.deletePasskey\")}\n        </Headline2>\n        {credentialType === \"security-key\"\n          ? t(\"texts.deleteSecurityKey\")\n          : t(\"texts.deletePasskey\")}\n        <br />\n        <Link\n          dangerous\n          flowAction={flowState.actions.webauthn_credential_delete}\n          onClick={(event) => onCredentialDelete(event, credential.id)}\n          loadingSpinnerPosition={\"right\"}\n        >\n          {t(\"labels.delete\")}\n        </Link>\n      </Paragraph>\n      <Paragraph>\n        <Headline2>{t(\"headlines.lastUsedAt\")}</Headline2>\n        {credential.last_used_at ? convertTime(credential.last_used_at) : \"-\"}\n      </Paragraph>\n      <Paragraph>\n        <Headline2>{t(\"headlines.createdAt\")}</Headline2>\n        {convertTime(credential.created_at)}\n      </Paragraph>\n    </>\n  );\n  return (\n    <Accordion\n      name={\n        credentialType === \"security-key\"\n          ? \"security-key-edit-dropdown\"\n          : \"passkey-edit-dropdown\"\n      }\n      columnSelector={labels}\n      data={credentials}\n      contentSelector={contents}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    />\n  );\n};\n\nexport default ListWebauthnCredentialsAccordion;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/ManageAuthAppDropdown.tsx",
    "content": "import { h } from \"preact\";\nimport { Dispatch, Fragment, SetStateAction, useContext } from \"preact/compat\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Paragraph from \"../paragraph/Paragraph\";\nimport Dropdown from \"./Dropdown\";\nimport Link from \"../link/Link\";\nimport Headline2 from \"../headline/Headline2\";\nimport styles from \"./styles.sass\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\ninterface Props {\n  checkedItemID?: string;\n  setCheckedItemID: Dispatch<SetStateAction<string>>;\n  flowState: State<\"profile_init\">;\n  onState(state: State<any>): Promise<void>;\n}\n\nconst ManageAuthAppDropdown = ({\n  checkedItemID,\n  setCheckedItemID,\n  flowState,\n  onState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n\n  const onAuthAppSetUp = async (event: Event) => {\n    event.preventDefault();\n    const nextState =\n      await flowState.actions.continue_to_otp_secret_creation.run(null, {\n        dispatchAfterStateChangeEvent: false,\n      });\n    return onState(nextState);\n  };\n\n  const onAuthAppRemove = async (event: Event) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.otp_secret_delete.run(null, {\n      dispatchAfterStateChangeEvent: false,\n    });\n    return onState(nextState);\n  };\n\n  const configuredLabel = (\n    <span className={styles.description}>\n      {flowState.payload.user.mfa_config?.auth_app_set_up ? (\n        <>\n          {\" -\"} {t(\"labels.configured\")}\n        </>\n      ) : null}\n    </span>\n  );\n\n  const title = (\n    <>\n      {t(\"labels.authenticatorAppManage\")} {configuredLabel}\n    </>\n  );\n\n  return (\n    <Dropdown\n      name={\"authenticator-app-manage-dropdown\"}\n      title={title}\n      checkedItemID={checkedItemID}\n      setCheckedItemID={setCheckedItemID}\n    >\n      <Headline2>\n        {t(\n          flowState.payload.user.mfa_config?.auth_app_set_up\n            ? \"headlines.authenticatorAppAlreadySetUp\"\n            : \"headlines.authenticatorAppNotSetUp\",\n        )}\n      </Headline2>\n      <Paragraph>\n        {t(\n          flowState.payload.user.mfa_config?.auth_app_set_up\n            ? \"texts.authenticatorAppAlreadySetUp\"\n            : \"texts.authenticatorAppNotSetUp\",\n        )}\n        <br />\n        {flowState.payload.user.mfa_config?.auth_app_set_up ? (\n          <Link\n            flowAction={flowState.actions.otp_secret_delete}\n            onClick={onAuthAppRemove}\n            loadingSpinnerPosition={\"right\"}\n            dangerous\n          >\n            {t(\"labels.delete\")}\n          </Link>\n        ) : (\n          <Link\n            flowAction={flowState.actions.continue_to_otp_secret_creation}\n            onClick={onAuthAppSetUp}\n            loadingSpinnerPosition={\"right\"}\n          >\n            {t(\"labels.authenticatorAppAdd\")}\n          </Link>\n        )}\n      </Paragraph>\n    </Dropdown>\n  );\n};\n\nexport default ManageAuthAppDropdown;\n"
  },
  {
    "path": "frontend/elements/src/components/accordion/styles.sass",
    "content": "@use '../../variables'\n@use '../../mixins'\n\n.accordion\n  @include mixins.font\n\n  width: 100%\n  overflow: hidden\n\n  .accordionItem\n    color: variables.$color\n\n    margin: .25rem 0\n    overflow: hidden\n\n    &.dropdown\n      margin: 0\n\n    .label\n      border-radius: variables.$border-radius\n      border-style: none\n\n      height: variables.$item-height\n      background: variables.$background-color\n\n      box-sizing: border-box\n      display: flex\n      align-items: center\n      justify-content: space-between\n      padding: 0 1rem\n      margin: 0\n      cursor: pointer\n      transition: all .35s\n\n      .labelText\n        white-space: nowrap\n        overflow: hidden\n        text-overflow: ellipsis\n\n        .description\n          color: variables.$color-shade-1\n\n      &.dropdown\n        margin: 0\n        color: variables.$link-color\n        justify-content: flex-start\n\n      &:hover\n        color: variables.$brand-contrast-color\n        background: variables.$brand-color-shade-1\n\n        .description\n          color: variables.$brand-contrast-color\n\n        &.dropdown\n          color: variables.$link-color\n          background: none\n\n      &:not(.dropdown)::after\n        content: \"\\276F\"\n        width: 1rem\n        text-align: center\n        transition: all .35s\n\n    .accordionInput\n      position: absolute\n      opacity: 0\n      z-index: -1\n\n      &:checked\n        + .label\n          color: variables.$brand-contrast-color\n          background: variables.$brand-color\n\n          .description\n            color: variables.$brand-contrast-color\n\n          &.dropdown\n            color: variables.$link-color\n            background: none\n\n          &:not(.dropdown)::after\n            transform: rotate(90deg)\n\n          ~ .accordionContent\n            margin: .25rem 1rem\n            opacity: 1\n            max-height: 100vh\n\n    .accordionContent\n      max-height: 0\n      margin: 0 1rem\n      opacity: 0\n      overflow: hidden\n      transition: all .35s\n\n      &.dropdownContent\n        border-style: none\n"
  },
  {
    "path": "frontend/elements/src/components/error/ErrorBox.tsx",
    "content": "import { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { FlowError, HankoError, State } from \"@teamhanko/hanko-frontend-sdk\";\nimport { useContext, useEffect } from \"preact/compat\";\nimport { AppContext } from \"../../contexts/AppProvider\";\n\nimport styles from \"./styles.sass\";\nimport Icon from \"../icons/Icon\";\n\ntype Props = {\n  state?: State<any>;\n  flowError?: FlowError;\n  error?: HankoError;\n};\n\nconst ErrorBox = ({ state, error, flowError }: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { uiState, setUIState } = useContext(AppContext);\n\n  useEffect(() => {\n    if (state?.error?.code == \"form_data_invalid_error\") {\n      for (const action of Object.values(state?.actions)) {\n        let relatedInputFound = false;\n        // @ts-ignore\n        for (const input of Object.values(action?.inputs)) {\n          // @ts-ignore\n          if (input.error?.code) {\n            // @ts-ignore\n            setUIState({ ...uiState, error: input.error });\n            relatedInputFound = true;\n            return;\n          }\n        }\n\n        if (!relatedInputFound) {\n          setUIState({ ...uiState, error: state.error });\n        }\n      }\n    } else if (state?.error) {\n      setUIState({ ...uiState, error: state?.error });\n    }\n  }, [state]);\n\n  return (\n    <section\n      part={\"error\"}\n      className={styles.errorBox}\n      hidden={!uiState.error?.code && !flowError?.code && !error}\n    >\n      <span>\n        <Icon name={\"exclamation\"} size={15} />\n      </span>\n      <span id=\"errorMessage\" part={\"error-text\"}>\n        {error\n          ? t(`errors.${error.code}`)\n          : t(`flowErrors.${uiState.error?.code || flowError?.code}`)}\n      </span>\n    </section>\n  );\n};\n\nexport default ErrorBox;\n"
  },
  {
    "path": "frontend/elements/src/components/error/ErrorMessage.tsx",
    "content": "import styles from \"./styles.sass\";\nimport { Fragment, useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { FlowError } from \"@teamhanko/hanko-frontend-sdk\";\n\ninterface Props {\n  flowError?: FlowError;\n}\n\nconst ErrorMessage = ({ flowError }: Props) => {\n  const { t } = useContext(TranslateContext);\n  return (\n    <>\n      {flowError ? (\n        <div className={styles.errorMessage}>\n          {t(`flowErrors.${flowError?.code}`)}\n        </div>\n      ) : null}\n    </>\n  );\n};\n\nexport default ErrorMessage;\n"
  },
  {
    "path": "frontend/elements/src/components/error/styles.sass",
    "content": "@use '../../variables'\n@use '../../mixins'\n\n.errorBox\n  @include mixins.font\n  @include mixins.border\n\n  color: variables.$error-color\n  background: variables.$background-color\n  margin: variables.$item-margin\n\n  display: flex\n  align-items: start\n  box-sizing: border-box\n\n  line-height: 1.5rem\n  padding: .25em\n  gap: .2em\n\n  &>span\n    display: inline-flex\n\n  &>span:first-child\n    padding: .25em 0 .25em .19em\n\n  &[hidden]\n    display: none\n\n.errorMessage\n  color: variables.$error-color\n\n\n\n"
  },
  {
    "path": "frontend/elements/src/components/form/Button.tsx",
    "content": "import { ComponentChildren } from \"preact\";\nimport {\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"preact/compat\";\n\nimport cx from \"classnames\";\n\nimport styles from \"./styles.sass\";\n\nimport LoadingSpinner from \"../icons/LoadingSpinner\";\nimport Icon, { IconName } from \"../icons/Icon\";\nimport { AppContext } from \"../../contexts/AppProvider\";\nimport { useFlowEffects } from \"../../hooks/UseFlowEffects\";\nimport { useFormContext } from \"./Form\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\ntype Props = {\n  title?: string;\n  showSuccessIcon?: boolean;\n  children: ComponentChildren;\n  secondary?: boolean;\n  dangerous?: boolean;\n  isLoading?: boolean;\n  isSuccess?: boolean;\n  disabled?: boolean;\n  autofocus?: boolean;\n  showLastUsed?: boolean;\n  onClick?: (event: Event) => void;\n  icon?: IconName;\n};\n\nconst Button = ({\n  title,\n  children,\n  secondary,\n  dangerous,\n  autofocus,\n  showLastUsed,\n  onClick,\n  icon,\n  showSuccessIcon,\n  ...props\n}: Props) => {\n  const ref = useRef(null);\n  const { uiState } = useContext(AppContext);\n  const { t } = useContext(TranslateContext);\n  const [isLoading, setIsLoading] = useState<boolean>(false);\n  const [isSuccess, setIsSuccess] = useState<boolean>(false);\n\n  const { flowAction } = useFormContext();\n\n  useFlowEffects(flowAction, setIsLoading, setIsSuccess);\n\n  useEffect(() => {\n    const { current: element } = ref;\n    if (element && autofocus) {\n      element.focus();\n    }\n  }, [autofocus]);\n\n  const success = useMemo(\n    () => showSuccessIcon && (isSuccess || props.isSuccess),\n    [isSuccess, props, showSuccessIcon],\n  );\n\n  const disabled = useMemo(\n    () => uiState.isDisabled || props.disabled,\n    [props, uiState],\n  );\n\n  return (\n    <button\n      // @ts-ignore\n      part={\n        dangerous\n          ? \"button dangerous-button\"\n          : secondary\n          ? \"button secondary-button\"\n          : \"button primary-button\"\n      }\n      title={title}\n      ref={ref}\n      type={\"submit\"}\n      disabled={disabled}\n      onClick={onClick}\n      className={cx(\n        styles.button,\n        dangerous\n          ? styles.dangerous\n          : secondary\n          ? styles.secondary\n          : styles.primary,\n      )}\n      data-bubble={showLastUsed ? t(\"labels.lastUsed\") : undefined}\n    >\n      <LoadingSpinner\n        isLoading={isLoading}\n        isSuccess={success}\n        secondary={true}\n        hasIcon={!!icon}\n        maxWidth\n      >\n        {icon ? (\n          <Icon name={icon} secondary={secondary} disabled={disabled} />\n        ) : null}\n        <div className={styles.caption}>\n          <span>{children}</span>\n        </div>\n      </LoadingSpinner>\n    </button>\n  );\n};\n\nexport default Button;\n"
  },
  {
    "path": "frontend/elements/src/components/form/Checkbox.tsx",
    "content": "import { h, InputHTMLAttributes } from \"preact\";\nimport styles from \"./styles.sass\";\nimport cx from \"classnames\";\nimport { useContext, useMemo } from \"preact/compat\";\nimport { AppContext } from \"../../contexts/AppProvider\";\n\ninterface Props extends InputHTMLAttributes {\n  label?: string;\n}\n\nconst Checkbox = ({ label, ...props }: Props) => {\n  const { uiState } = useContext(AppContext);\n\n  const disabled = useMemo(\n    () => uiState.isDisabled || props.disabled,\n    [props, uiState],\n  );\n\n  return (\n    <div className={styles.inputWrapper}>\n      <label className={styles.checkboxWrapper}>\n        <input\n          part={\"input checkbox-input\"}\n          type={\"checkbox\"}\n          aria-label={label}\n          className={styles.checkbox}\n          {...props}\n        />\n        <span className={cx(styles.label, disabled ? styles.disabled : null)}>\n          {label}\n        </span>\n      </label>\n    </div>\n  );\n};\n\nexport default Checkbox;\n"
  },
  {
    "path": "frontend/elements/src/components/form/CodeInput.tsx",
    "content": "import { h, InputHTMLAttributes } from \"preact\";\nimport {\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"preact/compat\";\n\nimport styles from \"./styles.sass\";\nimport { AppContext } from \"../../contexts/AppProvider\";\n\n// Inspired by https://github.com/devfolioco/react-otp-input\n\ninterface Props {\n  passcodeDigits: string[];\n  numberOfInputs?: number;\n  onInput?: (passcodeDigits: string[]) => void;\n  disabled?: boolean;\n}\n\ninterface DigitProps extends InputHTMLAttributes {\n  index: number;\n  focus: boolean;\n  digit: string;\n}\n\nconst Digit = ({ index, focus, digit = \"\", ...props }: DigitProps) => {\n  const ref = useRef(null);\n  const { uiState } = useContext(AppContext);\n\n  const focusInput = () => {\n    const { current: element } = ref;\n    if (element) {\n      element.focus();\n      element.select();\n    }\n  };\n\n  const disabled = useMemo(\n    () => uiState.isDisabled || props.disabled,\n    [props, uiState],\n  );\n\n  // Autofocus if it's the first input element\n  useEffect(() => {\n    if (index === 0) {\n      focusInput();\n    }\n  }, [index, props.disabled]);\n\n  // Focus the current input element\n  useMemo(() => {\n    if (focus) {\n      focusInput();\n    }\n  }, [focus]);\n\n  return (\n    <div className={styles.passcodeDigitWrapper}>\n      <input\n        {...props}\n        part={\"input passcode-input\"}\n        aria-label={`${props.name}-digit-${index + 1}`}\n        name={props.name + index.toString(10)}\n        type={\"text\"}\n        inputMode={\"numeric\"}\n        maxLength={1}\n        ref={ref}\n        value={digit.charAt(0)}\n        required={true}\n        className={styles.input}\n        disabled={disabled}\n      />\n    </div>\n  );\n};\n\nconst CodeInput = ({\n  passcodeDigits = [],\n  numberOfInputs = 6,\n  onInput,\n  disabled = false,\n}: Props) => {\n  const [activeInputIndex, setActiveInputIndex] = useState<number>(0);\n\n  // returns a copy of the digit array\n  const getPasscodeDigits = (): string[] => passcodeDigits.slice();\n\n  const focusNextInput = () => {\n    if (activeInputIndex < numberOfInputs - 1) {\n      setActiveInputIndex(activeInputIndex + 1);\n    }\n  };\n\n  const focusPrevInput = () => {\n    if (activeInputIndex > 0) {\n      setActiveInputIndex(activeInputIndex - 1);\n    }\n  };\n\n  const changeCodeAtFocus = (digit: string) => {\n    const digits = getPasscodeDigits();\n    digits[activeInputIndex] = digit.charAt(0);\n    onInput(digits);\n  };\n\n  const handleOnPaste = (event: ClipboardEvent) => {\n    event.preventDefault();\n    if (disabled) {\n      return;\n    }\n\n    // Get pastedData in an array of max size (num of inputs - current position)\n    const pastedData = event.clipboardData\n      .getData(\"text/plain\")\n      .slice(0, numberOfInputs - activeInputIndex)\n      .split(\"\");\n\n    const digits = getPasscodeDigits();\n    let nextActiveInput = activeInputIndex;\n\n    // Paste data from focused input onwards\n    for (let index = 0; index < numberOfInputs; ++index) {\n      if (index >= activeInputIndex && pastedData.length > 0) {\n        digits[index] = pastedData.shift();\n        nextActiveInput++;\n      }\n    }\n\n    setActiveInputIndex(nextActiveInput);\n    onInput(digits);\n  };\n\n  // Handle cases of backspace, delete, left arrow, right arrow, space\n  const handleOnKeyDown = (event: KeyboardEvent) => {\n    if (event.key === \"Backspace\") {\n      event.preventDefault();\n      changeCodeAtFocus(\"\");\n      focusPrevInput();\n    } else if (event.key === \"Delete\") {\n      event.preventDefault();\n      changeCodeAtFocus(\"\");\n    } else if (event.key === \"ArrowLeft\") {\n      event.preventDefault();\n      focusPrevInput();\n    } else if (event.key === \"ArrowRight\") {\n      event.preventDefault();\n      focusNextInput();\n    } else if (\n      event.key === \" \" ||\n      event.key === \"Spacebar\" ||\n      event.key === \"Space\"\n    ) {\n      event.preventDefault();\n    }\n  };\n\n  // The content may not have changed, but some input took place hence change the focus\n  const handleOnInput = (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      changeCodeAtFocus(event.target.value);\n    }\n    focusNextInput();\n  };\n\n  const handleOnFocus = (index: number) => {\n    setActiveInputIndex(index);\n  };\n\n  // Autofocus the first input when passcode has been reset\n  useEffect(() => {\n    if (passcodeDigits.length === 0) {\n      setActiveInputIndex(0);\n    }\n  }, [passcodeDigits]);\n\n  return (\n    <div className={styles.passcodeInputWrapper}>\n      {Array.from(Array(numberOfInputs)).map((_, index) => (\n        <Digit\n          name={\"passcode\"}\n          key={index}\n          index={index}\n          focus={activeInputIndex === index}\n          digit={passcodeDigits[index]}\n          onKeyDown={handleOnKeyDown}\n          onInput={handleOnInput}\n          onPaste={handleOnPaste}\n          onFocus={() => handleOnFocus(index)}\n          disabled={disabled}\n        />\n      ))}\n    </div>\n  );\n};\n\nexport default CodeInput;\n"
  },
  {
    "path": "frontend/elements/src/components/form/Form.tsx",
    "content": "import { ComponentChildren, toChildArray } from \"preact\";\n\nimport styles from \"./styles.sass\";\nimport cx from \"classnames\";\nimport { Action } from \"@teamhanko/hanko-frontend-sdk\";\nimport { useContext, createContext } from \"preact/compat\";\n\ntype Props = {\n  onSubmit?: (event: Event) => void;\n  children: ComponentChildren;\n  hidden?: boolean;\n  maxWidth?: boolean;\n  flowAction?: Action<any>;\n};\n\ntype FormContextType = {\n  flowAction?: Action<any>;\n};\n\nexport const FormContext = createContext<FormContextType>({});\n\nexport const useFormContext = () => useContext(FormContext);\n\nconst Form = ({\n  onSubmit,\n  children,\n  hidden = false,\n  maxWidth,\n  flowAction,\n}: Props) => {\n  const defaultOnSubmit = async (event: Event) => {\n    event.preventDefault();\n    return await flowAction.run();\n  };\n\n  // Cast Provider to any to bypass strict JSX return type check (TS2786)\n  // TODO: Find out why, we this need to be casted to any for the build to work.\n  const FormContextProviderAny = FormContext.Provider as any;\n\n  return (\n    <FormContextProviderAny value={{ flowAction }}>\n      {flowAction && flowAction.enabled && !hidden ? (\n        <form onSubmit={onSubmit || defaultOnSubmit} className={styles.form}>\n          <ul className={styles.ul}>\n            {toChildArray(children).map((child, index) => (\n              <li\n                part={\"form-item\"}\n                className={cx(styles.li, maxWidth ? styles.maxWidth : null)}\n                key={index}\n              >\n                {child}\n              </li>\n            ))}\n          </ul>\n        </form>\n      ) : null}\n    </FormContextProviderAny>\n  );\n};\n\nexport default Form;\n"
  },
  {
    "path": "frontend/elements/src/components/form/Input.tsx",
    "content": "import { h, InputHTMLAttributes } from \"preact\";\nimport { useContext, useEffect, useMemo, useRef } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { Input as FlowInput } from \"@teamhanko/hanko-frontend-sdk\";\nimport { AppContext } from \"../../contexts/AppProvider\";\nimport cx from \"classnames\";\n\nimport styles from \"./styles.sass\";\n\ninterface Props extends InputHTMLAttributes {\n  label?: string;\n  markOptional?: boolean;\n  markError?: boolean;\n  flowInput?: FlowInput<any>;\n}\n\nconst Input = ({ label, ...props }: Props) => {\n  const ref = useRef(null);\n  const { uiState } = useContext(AppContext);\n  const { t } = useContext(TranslateContext);\n\n  const disabled = useMemo(\n    () => uiState.isDisabled || props.disabled,\n    [props, uiState],\n  );\n\n  useEffect(() => {\n    const { current: element } = ref;\n    if (element && props.autofocus) {\n      element.focus();\n      element.select();\n    }\n  }, [props.autofocus]);\n\n  const placeholder = useMemo(() => {\n    if (props.markOptional && !props.flowInput?.required) {\n      return `${props.placeholder} (${t(\"labels.optional\")})`;\n    }\n    return props.placeholder;\n  }, [props.markOptional, props.placeholder, props.flowInput, t]);\n\n  return (\n    <div className={styles.inputWrapper}>\n      <input\n        part={\"input text-input\"}\n        required={props.flowInput?.required}\n        maxLength={props.flowInput?.max_length}\n        minLength={props.flowInput?.min_length}\n        hidden={props.flowInput?.hidden}\n        {...props}\n        ref={ref}\n        aria-label={placeholder}\n        placeholder={placeholder}\n        className={cx(\n          styles.input,\n          !!props.flowInput?.error && props.markError && styles.error,\n        )}\n        disabled={disabled}\n      />\n    </div>\n  );\n};\n\nexport default Input;\n"
  },
  {
    "path": "frontend/elements/src/components/form/styles.sass",
    "content": "@use '../../variables'\n@use '../../mixins'\n\n// Form Styles\n.form\n  display: flex\n  flex-grow: 1\n\n  .ul\n    flex-grow: 1\n    margin: variables.$item-margin\n    padding-inline-start: 0\n    list-style-type: none\n    display: flex\n    flex-wrap: wrap\n    gap: 1em\n\n  .li\n    display: flex\n    max-width: 100%\n    flex-grow: 1\n    flex-basis: min-content\n\n    &.maxWidth\n      min-width: 100%\n\n// Button Styles\n.button\n  @include mixins.font\n  @include mixins.border\n\n  white-space: nowrap\n  width: 100%\n  min-width: variables.$button-min-width\n  min-height: variables.$item-height\n  outline: none\n  cursor: pointer\n  transition: 0.1s ease-out\n  flex-grow: 1\n  flex-shrink: 1\n  display: inline-flex\n  position: relative\n\n  &[data-bubble]\n    &:before\n      @include mixins.font\n      @include mixins.border\n\n      padding: 2px 8px\n      font-size: 9px\n      line-height: normal\n      content: attr(data-bubble)\n      display: block\n      position: absolute\n      bottom: 80%\n      left: 80%\n      white-space: nowrap\n      width: max-content\n      text-align: center\n      background-color: inherit\n      color: variables.$brand-color-shade-1\n      border-color: variables.$brand-color-shade-1\n\n  &:disabled\n    cursor: default\n\n  &.primary\n    color: variables.$brand-contrast-color\n    background: variables.$brand-color\n    border-color: variables.$brand-color\n    justify-content: center\n\n  &.primary:hover\n    color: variables.$brand-contrast-color\n    background: variables.$brand-color-shade-1\n    border-color: variables.$brand-color\n\n  &.primary:focus\n    color: variables.$brand-contrast-color\n    background: variables.$brand-color\n    border-color: variables.$color\n\n  &.primary:disabled\n    color: variables.$color-shade-1\n    background: variables.$color-shade-2\n    border-color: variables.$color-shade-2\n\n  &.secondary\n    color: variables.$color\n    background: variables.$background-color\n    border-color: variables.$color-shade-1\n    justify-content: center\n\n  &.secondary:hover\n    color: variables.$color\n    background: variables.$color-shade-2\n    border-color: variables.$color\n\n  &.secondary:focus\n    color: variables.$color\n    background: variables.$background-color\n    border-color: variables.$brand-color\n\n  &.secondary:disabled\n    color: variables.$color-shade-1\n    background: variables.$color-shade-2\n    border-color: variables.$color-shade-1\n\n  &.dangerous\n    color: variables.$error-color\n    background: variables.$background-color\n    border-color: variables.$error-color\n    flex-grow: 0\n    width: auto\n\n.caption\n  flex-wrap: wrap\n  display: flex\n  justify-content: space-between\n  align-items: baseline\n\n// Input Styles\n.inputWrapper\n  flex-grow: 1\n  position: relative\n  display: flex\n  min-width: variables.$input-min-width\n  max-width: 100%\n\n.input\n  @include mixins.font\n  @include mixins.border\n\n  height: variables.$item-height\n  color: variables.$color\n  border-color: variables.$color-shade-1\n  background: variables.$background-color\n\n  &.error\n    border-color: variables.$error-color\n\n  padding: 0 .5rem\n  outline: none\n  width: 100%\n  box-sizing: border-box\n  transition: 0.1s ease-out\n\n  &:-webkit-autofill, &:-webkit-autofill:hover, &:-webkit-autofill:focus\n    -webkit-text-fill-color: variables.$color\n    -webkit-box-shadow: 0 0 0 50px variables.$background-color inset\n\n  // Removes native \"clear text\" and \"password reveal\" buttons from Edge\n  &::-ms-reveal, &::-ms-clear\n    display: none\n\n  &::placeholder\n    color: variables.$color-shade-1\n\n  &:focus\n    color: variables.$color\n    border-color: variables.$color\n\n  &:disabled\n    color: variables.$color-shade-1\n    background: variables.$color-shade-2\n    border-color: variables.$color-shade-1\n\n.passcodeInputWrapper\n  flex-grow: 1\n  min-width: variables.$input-min-width\n  max-width: fit-content\n  position: relative\n  display: flex\n  justify-content: space-between\n\n  .passcodeDigitWrapper\n    flex-grow: 1\n    margin: 0 .5rem 0 0\n\n    &:last-child\n      margin: 0\n\n    .input\n      text-align: center\n\n// Checkmark Styles\n\n.checkboxWrapper\n  @include mixins.font\n\n  color: variables.$color\n  align-items: center\n  display: flex\n\n  .label\n    color: inherit\n    padding-left: .5rem\n    cursor: pointer\n\n    &.disabled\n      cursor: default\n      color: variables.$color-shade-1\n\n  .checkbox\n    border: currentColor solid 1px\n    border-radius: 0.15em\n    appearance: none\n    -webkit-appearance: none\n    width: 1.1rem\n    height: 1.1rem\n    margin: 0\n    color: currentColor\n    background-color: variables.$background-color\n    font: inherit\n    box-shadow: none\n    display: inline-flex\n    place-content: center\n    cursor: pointer\n\n    &:checked\n      background-color: variables.$color\n\n    &:disabled\n      cursor: default\n      background-color: variables.$color-shade-2\n      border-color: variables.$color-shade-1\n\n    &:checked:after\n      content: '\\2713'\n      color: variables.$background-color\n      position: absolute\n      line-height: 1.1rem\n\n    &:disabled:after\n      color: variables.$color-shade-1\n"
  },
  {
    "path": "frontend/elements/src/components/headline/Headline1.tsx",
    "content": "import { ComponentChildren } from \"preact\";\n\nimport cx from \"classnames\";\n\nimport styles from \"./styles.sass\";\n\ntype Props = {\n  children: ComponentChildren;\n};\n\nconst Headline1 = ({ children }: Props) => {\n  return (\n    <h1 part={\"headline1\"} className={cx(styles.headline, styles.grade1)}>\n      {children}\n    </h1>\n  );\n};\n\nexport default Headline1;\n"
  },
  {
    "path": "frontend/elements/src/components/headline/Headline2.tsx",
    "content": "import { ComponentChildren } from \"preact\";\n\nimport cx from \"classnames\";\n\nimport styles from \"./styles.sass\";\n\ntype Props = {\n  children: ComponentChildren;\n};\n\nconst Headline2 = ({ children }: Props) => {\n  return (\n    <h2 part={\"headline2\"} className={cx(styles.headline, styles.grade2)}>\n      {children}\n    </h2>\n  );\n};\n\nexport default Headline2;\n"
  },
  {
    "path": "frontend/elements/src/components/headline/styles.sass",
    "content": "@use '../../variables'\n@use '../../mixins'\n\n.headline\n  color: variables.$color\n  font-family: variables.$font-family\n  text-align: left\n  letter-spacing: 0\n  font-style: normal\n  line-height: 1.1\n\n  &.grade1\n    font-size: variables.$headline1-font-size\n    font-weight: variables.$headline1-font-weight\n    margin: variables.$headline1-margin\n\n  &.grade2\n    font-size: variables.$headline2-font-size\n    font-weight: variables.$headline2-font-weight\n    margin: variables.$headline2-margin\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Apple.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst Apple = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-apple\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={size}\n      height={size}\n      viewBox=\"20.5 16 15 19\"\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M28.2226562,20.3846154 C29.0546875,20.3846154 30.0976562,19.8048315 30.71875,19.0317864 C31.28125,18.3312142 31.6914062,17.352829 31.6914062,16.3744437 C31.6914062,16.2415766 31.6796875,16.1087095 31.65625,16 C30.7304687,16.0362365 29.6171875,16.640178 28.9492187,17.4494596 C28.421875,18.06548 27.9414062,19.0317864 27.9414062,20.0222505 C27.9414062,20.1671964 27.9648438,20.3121424 27.9765625,20.3604577 C28.0351562,20.3725366 28.1289062,20.3846154 28.2226562,20.3846154 Z M25.2929688,35 C26.4296875,35 26.9335938,34.214876 28.3515625,34.214876 C29.7929688,34.214876 30.109375,34.9758423 31.375,34.9758423 C32.6171875,34.9758423 33.4492188,33.792117 34.234375,32.6325493 C35.1132812,31.3038779 35.4765625,29.9993643 35.5,29.9389701 C35.4179688,29.9148125 33.0390625,28.9122695 33.0390625,26.0979021 C33.0390625,23.6579784 34.9140625,22.5588048 35.0195312,22.474253 C33.7773438,20.6382708 31.890625,20.5899555 31.375,20.5899555 C29.9804688,20.5899555 28.84375,21.4596313 28.1289062,21.4596313 C27.3554688,21.4596313 26.3359375,20.6382708 25.1289062,20.6382708 C22.8320312,20.6382708 20.5,22.5950413 20.5,26.2911634 C20.5,28.5861411 21.3671875,31.013986 22.4335938,32.5842339 C23.3476562,33.9129053 24.1445312,35 25.2929688,35 Z\" />\n    </svg>\n  );\n};\n\nexport default Apple;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Checkmark.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport styles from \"./styles.sass\";\nimport cx from \"classnames\";\n\nconst Checkmark = ({ secondary, size, fadeOut, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-checkmark\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"4 4 40 40\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.checkmark,\n        secondary && styles.secondary,\n        fadeOut && styles.fadeOut,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M21.05 33.1 35.2 18.95l-2.3-2.25-11.85 11.85-6-6-2.25 2.25ZM24 44q-4.1 0-7.75-1.575-3.65-1.575-6.375-4.3-2.725-2.725-4.3-6.375Q4 28.1 4 24q0-4.15 1.575-7.8 1.575-3.65 4.3-6.35 2.725-2.7 6.375-4.275Q19.9 4 24 4q4.15 0 7.8 1.575 3.65 1.575 6.35 4.275 2.7 2.7 4.275 6.35Q44 19.85 44 24q0 4.1-1.575 7.75-1.575 3.65-4.275 6.375t-6.35 4.3Q28.15 44 24 44Zm0-3q7.1 0 12.05-4.975Q41 31.05 41 24q0-7.1-4.95-12.05Q31.1 7 24 7q-7.05 0-12.025 4.95Q7 16.9 7 24q0 7.05 4.975 12.025Q16.95 41 24 41Zm0-17Z\" />\n    </svg>\n  );\n};\n\nexport default Checkmark;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Copy.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst Copy = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 -960 960 960\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360Zm0-80h360v-480H360v480ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Zm160-240v-480 480Z\" />\n    </svg>\n  );\n};\n\nexport default Copy;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/CustomProvider.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst CustomProvider = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-custom-provider\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M0 0h24v24H0z\" fill=\"none\" />\n      <path d=\"M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z\" />\n    </svg>\n  );\n};\n\nexport default CustomProvider;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Discord.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst Discord = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-discord\"\n      fill=\"#fff\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={size}\n      height={size}\n      viewBox=\"0 0 127.14 96.36\"\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z\" />\n    </svg>\n  );\n};\n\nexport default Discord;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/ExclamationMark.tsx",
    "content": "import styles from \"./styles.sass\";\nimport { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\n\nconst ExclamationMark = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-exclamation\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"5 2 13 20\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.exclamationMark,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\" />\n    </svg>\n  );\n};\n\nexport default ExclamationMark;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Facebook.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst Facebook = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 666.66668 666.66717\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <defs id=\"defs13\">\n        <clipPath clipPathUnits=\"userSpaceOnUse\" id=\"clipPath25\">\n          <path d=\"M 0,700 H 700 V 0 H 0 Z\" id=\"path23\" />\n        </clipPath>\n      </defs>\n      <g\n        id=\"g17\"\n        transform=\"matrix(1.3333333,0,0,-1.3333333,-133.33333,799.99999)\"\n      >\n        <g id=\"g19\">\n          <g id=\"g21\" clipPath=\"url(#clipPath25)\">\n            <g id=\"g27\" transform=\"translate(600,350)\">\n              <path\n                className={cx(\n                  styles.facebookIcon,\n                  disabled ? styles.disabledOutline : styles.outline,\n                )}\n                d=\"m 0,0 c 0,138.071 -111.929,250 -250,250 -138.071,0 -250,-111.929 -250,-250 0,-117.245 80.715,-215.622 189.606,-242.638 v 166.242 h -51.552 V 0 h 51.552 v 32.919 c 0,85.092 38.508,124.532 122.048,124.532 15.838,0 43.167,-3.105 54.347,-6.211 V 81.986 c -5.901,0.621 -16.149,0.932 -28.882,0.932 -40.993,0 -56.832,-15.528 -56.832,-55.9 V 0 h 81.659 l -14.028,-76.396 h -67.631 V -248.169 C -95.927,-233.218 0,-127.818 0,0\"\n                id=\"path29\"\n              />\n            </g>\n            <g id=\"g31\" transform=\"translate(447.9175,273.6036)\">\n              <path\n                className={cx(\n                  styles.facebookIcon,\n                  disabled ? styles.disabledLetter : styles.letter,\n                )}\n                d=\"M 0,0 14.029,76.396 H -67.63 v 27.019 c 0,40.372 15.838,55.899 56.831,55.899 12.733,0 22.981,-0.31 28.882,-0.931 v 69.253 c -11.18,3.106 -38.509,6.212 -54.347,6.212 -83.539,0 -122.048,-39.441 -122.048,-124.533 V 76.396 h -51.552 V 0 h 51.552 v -166.242 c 19.343,-4.798 39.568,-7.362 60.394,-7.362 10.254,0 20.358,0.632 30.288,1.831 L -67.63,0 Z\"\n                id=\"path33\"\n              />\n            </g>\n          </g>\n        </g>\n      </g>\n    </svg>\n  );\n};\n\nexport default Facebook;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/GitHub.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst GitHub = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-github\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      fill=\"#fff\"\n      viewBox=\"0 0 97.63 96\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z\" />{\" \"}\n    </svg>\n  );\n};\n\nexport default GitHub;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Google.tsx",
    "content": "import styles from \"./styles.sass\";\nimport { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\n\nconst Google = ({ size, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-google\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      width={size}\n      height={size}\n      className={styles.googleIcon}\n    >\n      <path\n        className={cx(\n          styles.googleIcon,\n          disabled ? styles.disabled : styles.blue,\n        )}\n        d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\"\n      />\n      <path\n        className={cx(\n          styles.googleIcon,\n          disabled ? styles.disabled : styles.green,\n        )}\n        d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\"\n      />\n      <path\n        className={cx(\n          styles.googleIcon,\n          disabled ? styles.disabled : styles.yellow,\n        )}\n        d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\"\n      />\n      <path\n        className={cx(\n          styles.googleIcon,\n          disabled ? styles.disabled : styles.red,\n        )}\n        d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\"\n      />\n      <path d=\"M1 1h22v22H1z\" fill=\"none\" />\n    </svg>\n  );\n};\n\nexport default Google;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Icon.tsx",
    "content": "import * as icons from \"./icons\";\n\nexport type IconName = keyof typeof icons;\n\nexport type IconProps = {\n  secondary?: boolean;\n  fadeOut?: boolean;\n  disabled?: boolean;\n  size?: number;\n};\n\ntype Props = IconProps & {\n  name: IconName;\n};\n\nconst Icon = ({ name, secondary, size = 18, fadeOut, disabled }: Props) => {\n  const Ico = icons[name];\n\n  return (\n    <Ico\n      size={size}\n      secondary={secondary}\n      fadeOut={fadeOut}\n      disabled={disabled}\n    />\n  );\n};\n\nexport default Icon;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/LinkedIn.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst LinkedIn = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-linkedin\"\n      fill=\"#fff\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={size}\n      viewBox=\"0 0 24 24\"\n      height={size}\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M20.5 2h-17A1.5 1.5 0 002 3.5v17A1.5 1.5 0 003.5 22h17a1.5 1.5 0 001.5-1.5v-17A1.5 1.5 0 0020.5 2zM8 19H5v-9h3zM6.5 8.25A1.75 1.75 0 118.3 6.5a1.78 1.78 0 01-1.8 1.75zM19 19h-3v-4.74c0-1.42-.6-1.93-1.38-1.93A1.74 1.74 0 0013 14.19a.66.66 0 000 .14V19h-3v-9h2.9v1.3a3.11 3.11 0 012.7-1.4c1.55 0 3.36.86 3.36 3.66z\" />\n    </svg>\n  );\n};\n\nexport default LinkedIn;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/LoadingSpinner.tsx",
    "content": "import { ComponentChildren, Fragment } from \"preact\";\nimport styles from \"./styles.sass\";\nimport Icon from \"./Icon\";\nimport cx from \"classnames\";\n\nexport type Props = {\n  children?: ComponentChildren;\n  isLoading?: boolean;\n  isSuccess?: boolean;\n  fadeOut?: boolean;\n  secondary?: boolean;\n  hasIcon?: boolean;\n  maxWidth?: boolean;\n};\n\nconst LoadingSpinner = ({\n  children,\n  isLoading,\n  isSuccess,\n  fadeOut,\n  secondary,\n  hasIcon,\n  maxWidth,\n}: Props) => {\n  return (\n    <>\n      {isLoading ? (\n        <div\n          className={cx(\n            styles.loadingSpinnerWrapper,\n            styles.centerContent,\n            maxWidth && styles.maxWidth,\n          )}\n        >\n          <Icon name={\"spinner\"} secondary={secondary} />\n        </div>\n      ) : isSuccess ? (\n        <div\n          className={cx(\n            styles.loadingSpinnerWrapper,\n            styles.centerContent,\n            maxWidth && styles.maxWidth,\n          )}\n        >\n          <Icon name={\"checkmark\"} secondary={secondary} fadeOut={fadeOut} />\n        </div>\n      ) : (\n        <div\n          className={\n            hasIcon\n              ? styles.loadingSpinnerWrapperIcon\n              : styles.loadingSpinnerWrapper\n          }\n        >\n          {children}\n        </div>\n      )}\n    </>\n  );\n};\n\nexport default LoadingSpinner;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Mail.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst Mail = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-mail\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={size}\n      height={size}\n      viewBox=\"0 -960 960 960\"\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z\" />\n    </svg>\n  );\n};\n\nexport default Mail;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Microsoft.tsx",
    "content": "import styles from \"./styles.sass\";\nimport { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\n\nconst Microsoft = ({ size, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-microsoft\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      width={size}\n      height={size}\n      className={styles.microsoftIcon}\n    >\n      <rect\n        className={cx(\n          styles.microsoftIcon,\n          disabled ? styles.disabled : styles.blue,\n        )}\n        x=\"1\"\n        y=\"1\"\n        width=\"9\"\n        height=\"9\"\n      />\n      <rect\n        className={cx(\n          styles.microsoftIcon,\n          disabled ? styles.disabled : styles.green,\n        )}\n        x=\"1\"\n        y=\"11\"\n        width=\"9\"\n        height=\"9\"\n      />\n      <rect\n        className={cx(\n          styles.microsoftIcon,\n          disabled ? styles.disabled : styles.yellow,\n        )}\n        x=\"11\"\n        y=\"1\"\n        width=\"9\"\n        height=\"9\"\n      />\n      <rect\n        className={cx(\n          styles.microsoftIcon,\n          disabled ? styles.disabled : styles.red,\n        )}\n        x=\"11\"\n        y=\"11\"\n        width=\"9\"\n        height=\"9\"\n      />\n    </svg>\n  );\n};\n\nexport default Microsoft;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Passkey.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport styles from \"./styles.sass\";\nimport cx from \"classnames\";\n\nconst Passkey = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-passkey\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"3 1.5 19.5 19\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <g id=\"icon-passkey-all\">\n        <circle id=\"icon-passkey-head\" cx=\"10.5\" cy=\"6\" r=\"4.5\" />\n        <path\n          id=\"icon-passkey-key\"\n          d=\"M22.5,10.5a3.5,3.5,0,1,0-5,3.15V19L19,20.5,21.5,18,20,16.5,21.5,15l-1.24-1.24A3.5,3.5,0,0,0,22.5,10.5Zm-3.5,0a1,1,0,1,1,1-1A1,1,0,0,1,19,10.5Z\"\n        />\n        <path\n          id=\"icon-passkey-body\"\n          d=\"M14.44,12.52A6,6,0,0,0,12,12H9a6,6,0,0,0-6,6v2H16V14.49A5.16,5.16,0,0,1,14.44,12.52Z\"\n        />\n      </g>\n    </svg>\n  );\n};\n\nexport default Passkey;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Password.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst Password = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-password\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={size}\n      height={size}\n      viewBox=\"0 -960 960 960\"\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M80-200v-80h800v80H80Zm46-242-52-30 34-60H40v-60h68l-34-58 52-30 34 58 34-58 52 30-34 58h68v60h-68l34 60-52 30-34-60-34 60Zm320 0-52-30 34-60h-68v-60h68l-34-58 52-30 34 58 34-58 52 30-34 58h68v60h-68l34 60-52 30-34-60-34 60Zm320 0-52-30 34-60h-68v-60h68l-34-58 52-30 34 58 34-58 52 30-34 58h68v60h-68l34 60-52 30-34-60-34 60Z\" />\n    </svg>\n  );\n};\n\nexport default Password;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/QRCodeScanner.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst QRCodeScanner = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 -960 960 960\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M80-680v-200h200v80H160v120H80Zm0 600v-200h80v120h120v80H80Zm600 0v-80h120v-120h80v200H680Zm120-600v-120H680v-80h200v200h-80ZM700-260h60v60h-60v-60Zm0-120h60v60h-60v-60Zm-60 60h60v60h-60v-60Zm-60 60h60v60h-60v-60Zm-60-60h60v60h-60v-60Zm120-120h60v60h-60v-60Zm-60 60h60v60h-60v-60Zm-60-60h60v60h-60v-60Zm240-320v240H520v-240h240ZM440-440v240H200v-240h240Zm0-320v240H200v-240h240Zm-60 500v-120H260v120h120Zm0-320v-120H260v120h120Zm320 0v-120H580v120h120Z\" />\n    </svg>\n  );\n};\n\nexport default QRCodeScanner;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/SecurityKey.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport cx from \"classnames\";\nimport styles from \"./styles.sass\";\n\nconst SecurityKey = ({ size, secondary, disabled }: IconProps) => {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 -960 960 960\"\n      width={size}\n      height={size}\n      className={cx(\n        styles.icon,\n        secondary && styles.secondary,\n        disabled && styles.disabled,\n      )}\n    >\n      <path d=\"M280-240q-100 0-170-70T40-480q0-100 70-170t170-70q66 0 121 33t87 87h432v240h-80v120H600v-120H488q-32 54-87 87t-121 33Zm0-80q66 0 106-40.5t48-79.5h246v120h80v-120h80v-80H434q-8-39-48-79.5T280-640q-66 0-113 47t-47 113q0 66 47 113t113 47Zm0-80q33 0 56.5-23.5T360-480q0-33-23.5-56.5T280-560q-33 0-56.5 23.5T200-480q0 33 23.5 56.5T280-400Zm0-80Z\" />\n    </svg>\n  );\n};\n\nexport default SecurityKey;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/Spinner.tsx",
    "content": "import { IconProps } from \"./Icon\";\nimport styles from \"./styles.sass\";\nimport cx from \"classnames\";\n\nconst Spinner = ({ size, disabled }: IconProps) => {\n  return (\n    <svg\n      id=\"icon-spinner\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      width={size}\n      height={size}\n      className={cx(styles.loadingSpinner, disabled && styles.disabled)}\n    >\n      <path\n        d=\"M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z\"\n        opacity=\".25\"\n      />\n      <path d=\"M10.72,19.9a8,8,0,0,1-6.5-9.79A7.77,7.77,0,0,1,10.4,4.16a8,8,0,0,1,9.49,6.52A1.54,1.54,0,0,0,21.38,12h.13a1.37,1.37,0,0,0,1.38-1.54,11,11,0,1,0-12.7,12.39A1.54,1.54,0,0,0,12,21.34h0A1.47,1.47,0,0,0,10.72,19.9Z\" />\n    </svg>\n  );\n};\n\nexport default Spinner;\n"
  },
  {
    "path": "frontend/elements/src/components/icons/icons.ts",
    "content": "import { default as apple } from \"./Apple\";\nimport { default as checkmark } from \"./Checkmark\";\nimport { default as copy } from \"./Copy\";\nimport { default as customProvider } from \"./CustomProvider\";\nimport { default as discord } from \"./Discord\";\nimport { default as exclamation } from \"./ExclamationMark\";\nimport { default as facebook } from \"./Facebook\";\nimport { default as github } from \"./GitHub\";\nimport { default as google } from \"./Google\";\nimport { default as linkedin } from \"./LinkedIn\";\nimport { default as mail } from \"./Mail\";\nimport { default as microsoft } from \"./Microsoft\";\nimport { default as passkey } from \"./Passkey\";\nimport { default as password } from \"./Password\";\nimport { default as qrCodeScanner } from \"./QRCodeScanner\";\nimport { default as securityKey } from \"./SecurityKey\";\nimport { default as spinner } from \"./Spinner\";\n\nexport {\n  apple,\n  checkmark,\n  copy,\n  customProvider,\n  discord,\n  exclamation,\n  facebook,\n  github,\n  google,\n  linkedin,\n  mail,\n  microsoft,\n  passkey,\n  password,\n  qrCodeScanner,\n  securityKey,\n  spinner,\n};\n"
  },
  {
    "path": "frontend/elements/src/components/icons/styles.sass",
    "content": "@use '../../variables'\n\n.icon\n  display: inline-block\n  fill: variables.$brand-contrast-color\n  width: 18px\n\n  &.secondary\n    fill: variables.$color\n\n  &.disabled\n    fill: variables.$color-shade-1\n\n// Checkmark Styles\n\n.checkmark\n  @extend .icon\n  fill: variables.$brand-color\n\n  &.secondary\n    fill: variables.$color-shade-1\n\n  &.fadeOut\n    animation: fadeOut ease-out 1.5s forwards !important\n\n@keyframes fadeOut\n  0%\n    opacity: 1\n\n  100%\n    opacity: 0\n\n// ExclamationMark Styles\n\n.exclamationMark\n  @extend .icon\n  fill: variables.$error-color\n\n// Loading Spinner Styles\n\n.loadingSpinnerWrapperIcon\n  @extend .loadingSpinnerWrapper\n  width: 100%\n  column-gap: 10px\n  margin-left: 10px\n\n.loadingSpinnerWrapper\n  display: inline-flex\n  align-items: center\n  height: 100%\n  margin: 0 5px\n  justify-content: inherit\n  flex-wrap: inherit\n\n  &.centerContent\n    justify-content: center\n\n  &.maxWidth\n    width: 100%\n\n  .loadingSpinner\n    @extend .icon\n    fill: variables.$brand-color\n    animation: spin 500ms ease-in-out infinite\n\n  &.secondary\n    fill: variables.$color-shade-1\n\n@keyframes spin\n  0%\n    transform: rotate(0deg)\n\n  100%\n    transform: rotate(360deg)\n\n// Google Styles\n\n.googleIcon\n  &.disabled\n    fill: variables.$color-shade-1\n\n  &.blue\n    fill: #4285F4\n  &.green\n    fill: #34A853\n  &.yellow\n    fill: #FBBC05\n  &.red\n    fill: #EA4335\n\n.microsoftIcon\n  &.disabled\n    fill: variables.$color-shade-1\n\n  &.blue\n    fill: #00A4EF\n  &.green\n    fill: #7FBA00\n  &.yellow\n    fill: #FFB900\n  &.red\n    fill: #F25022\n\n.facebookIcon\n  &.outline\n    fill: #0866FF\n  &.disabledOutline\n    fill: variables.$color-shade-1\n  &.letter\n    fill: #FFFFFF\n  &.disabledLetter\n    fill: variables.$color-shade-2\n"
  },
  {
    "path": "frontend/elements/src/components/link/Link.tsx",
    "content": "import { ButtonHTMLAttributes, Fragment, h } from \"preact\";\n\nimport cx from \"classnames\";\n\nimport LoadingSpinner, {\n  Props as LoadingSpinnerProps,\n} from \"../icons/LoadingSpinner\";\n\nimport styles from \"./styles.sass\";\nimport { useCallback, useContext, useMemo, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { AppContext } from \"../../contexts/AppProvider\";\nimport { useFlowEffects } from \"../../hooks/UseFlowEffects\";\nimport { Action } from \"@teamhanko/hanko-frontend-sdk\";\n\ntype LoadingSpinnerPosition = \"left\" | \"right\";\n\nexport interface Props extends LoadingSpinnerProps, ButtonHTMLAttributes {\n  onClick?(event: Event): void;\n  dangerous?: boolean;\n  loadingSpinnerPosition?: LoadingSpinnerPosition;\n  flowAction?: Action<any>;\n}\n\nconst Link = ({\n  loadingSpinnerPosition,\n  dangerous = false,\n  onClick,\n  flowAction,\n  ...props\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { uiState } = useContext(AppContext);\n  const [confirmationActive, setConfirmationActive] = useState<boolean>();\n  const [isLoading, setIsLoading] = useState<boolean>(false);\n  const [isSuccess, setIsSuccess] = useState<boolean>(false);\n\n  onClick ||= async (e: Event) => {\n    e.preventDefault();\n    return await flowAction?.run();\n  };\n\n  useFlowEffects(flowAction, setIsLoading, setIsSuccess);\n\n  let timeoutID: number;\n\n  const dangerousOnClick = (event: Event) => {\n    event.preventDefault();\n    setConfirmationActive(true);\n  };\n\n  const onCancel = (event: Event) => {\n    event.preventDefault();\n    setConfirmationActive(false);\n  };\n\n  const loading = useMemo(\n    () => isLoading || props.isLoading,\n    [isLoading, props],\n  );\n\n  const success = useMemo(\n    () => isSuccess || props.isSuccess,\n    [isSuccess, props],\n  );\n\n  const hidden = useMemo(\n    () => (flowAction && !flowAction.enabled) || props.hidden,\n    [flowAction, props],\n  );\n\n  const onConfirmation = useCallback(\n    (event: Event) => {\n      event.preventDefault();\n      setConfirmationActive(false);\n      onClick(event);\n    },\n    [onClick],\n  );\n\n  const renderLink = useCallback(\n    () =>\n      !hidden ? (\n        <>\n          {confirmationActive ? (\n            <>\n              <Link onClick={onConfirmation}>{t(\"labels.yes\")}</Link>\n              &nbsp;/&nbsp;\n              <Link onClick={onCancel}>{t(\"labels.no\")}</Link>\n              &nbsp;\n            </>\n          ) : null}\n          <button\n            {...props}\n            onClick={dangerous ? dangerousOnClick : onClick}\n            disabled={\n              confirmationActive || props.disabled || uiState.isDisabled\n            }\n            part={\"link\"}\n            className={cx(styles.link, dangerous ? styles.danger : null)}\n          >\n            {props.children}\n          </button>\n        </>\n      ) : null,\n    [\n      hidden,\n      uiState,\n      confirmationActive,\n      dangerous,\n      onClick,\n      onConfirmation,\n      props,\n      t,\n    ],\n  );\n\n  const handleOnMouseEnter = () => {\n    if (timeoutID) window.clearTimeout(timeoutID);\n  };\n\n  const handleOnMouseLeave = () => {\n    timeoutID = window.setTimeout(() => {\n      setConfirmationActive(false);\n    }, 1000);\n  };\n\n  return (\n    <>\n      <span\n        className={cx(\n          styles.linkWrapper,\n          loadingSpinnerPosition === \"right\" ? styles.reverse : null,\n        )}\n        onMouseEnter={handleOnMouseEnter}\n        onMouseLeave={handleOnMouseLeave}\n      >\n        {!confirmationActive && (loading || success) ? (\n          <>\n            <LoadingSpinner\n              isLoading={loading}\n              isSuccess={success}\n              secondary={props.secondary}\n              fadeOut\n            />\n            {renderLink()}\n          </>\n        ) : (\n          <>{renderLink()}</>\n        )}\n      </span>\n    </>\n  );\n};\n\nexport default Link;\n"
  },
  {
    "path": "frontend/elements/src/components/link/styles.sass",
    "content": "@use \"../../variables\"\n@use \"../../mixins\"\n\n.link\n  @include mixins.font\n\n  color: variables.$link-color\n  text-decoration: variables.$link-text-decoration\n  cursor: pointer\n  background: none!important\n  border: none\n  padding: 0!important\n  transition: all .1s\n\n  &:hover\n    text-decoration: variables.$link-text-decoration-hover\n\n  &:disabled\n    color: variables.$color!important\n    pointer-events: none\n    cursor: default\n\n  &.danger\n    color: variables.$error-color\n\n.linkWrapper\n  display: inline-flex\n  flex-direction: row\n  justify-content: space-between\n  align-items: center\n  overflow: hidden\n\n  &.reverse\n    flex-direction: row-reverse\n\n"
  },
  {
    "path": "frontend/elements/src/components/otp/OTPCreationDetails.tsx",
    "content": "import { h } from \"preact\";\nimport styles from \"./styles.sass\";\n\nimport Clipboard from \"../wrapper/Clipboard\";\nimport Spacer from \"../spacer/Spacer\";\nimport { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\ntype Props = {\n  src: string;\n  secret: string;\n};\n\nconst OTPCreationDetails = ({ src, secret }: Props) => {\n  const { t } = useContext(TranslateContext);\n  return (\n    <div className={styles.otpCreationDetails}>\n      <img alt={\"QR-Code\"} src={src} />\n      <Spacer />\n      <Clipboard text={secret}>{t(\"texts.otpSecretKey\")}</Clipboard>\n      <div>{secret}</div>\n    </div>\n  );\n};\n\nexport default OTPCreationDetails;\n"
  },
  {
    "path": "frontend/elements/src/components/otp/styles.sass",
    "content": "@use \"../../variables\"\n@use \"../../mixins\"\n\n.otpCreationDetails\n  @include mixins.font\n\n  color: variables.$color\n  margin: variables.$item-margin\n  display: flex\n  justify-content: center\n  align-items: center\n  flex-direction: column\n  font-size: smaller\n\n\n\n"
  },
  {
    "path": "frontend/elements/src/components/paragraph/Paragraph.tsx",
    "content": "import { ComponentChildren } from \"preact\";\n\nimport styles from \"./styles.sass\";\nimport cx from \"classnames\";\n\ntype Props = {\n  hidden?: boolean;\n  children: ComponentChildren;\n  center?: boolean;\n};\n\nconst Paragraph = ({ children, hidden, center }: Props) => {\n  return !hidden ? (\n    <p\n      part={\"paragraph\"}\n      className={cx(\n        styles.paragraph,\n        center && styles.center,\n        center && styles.column,\n      )}\n    >\n      {children}\n    </p>\n  ) : null;\n};\n\nexport default Paragraph;\n"
  },
  {
    "path": "frontend/elements/src/components/paragraph/styles.sass",
    "content": "@use \"../../variables\"\n@use \"../../mixins\"\n\n.paragraph\n  @include mixins.font\n\n  color: variables.$color\n  margin: variables.$item-margin\n\n  text-align: left\n  word-break: break-word\n\n  &.center\n    align-items: center\n\n  &.column\n    display: flex\n    flex-direction: column\n    width: 100%\n"
  },
  {
    "path": "frontend/elements/src/components/spacer/Divider.tsx",
    "content": "import { ComponentChildren } from \"preact\";\n\nimport styles from \"./styles.sass\";\n\ninterface Props {\n  children?: ComponentChildren;\n  hidden?: boolean;\n}\n\nconst Divider = ({ children, hidden }: Props) => {\n  return !hidden ? (\n    <section part={\"divider\"} className={styles.divider}>\n      <div part={\"divider-line\"} className={styles.line} />\n      {children ? (\n        <div part={\"divider-text\"} class={styles.text}>\n          {children}\n        </div>\n      ) : null}\n      <div part={\"divider-line\"} className={styles.line} />\n    </section>\n  ) : null;\n};\n\nexport default Divider;\n"
  },
  {
    "path": "frontend/elements/src/components/spacer/Spacer.tsx",
    "content": "import styles from \"./styles.sass\";\n\nconst Spacer = () => {\n  return <section className={styles.spacer} />;\n};\n\nexport default Spacer;\n"
  },
  {
    "path": "frontend/elements/src/components/spacer/styles.sass",
    "content": "@use '../../variables'\n@use '../../mixins'\n\n.spacer\n  height: 1em\n\n.divider\n  @include mixins.font\n\n  display: flex\n  visibility: variables.$divider-visibility\n  color: variables.$color-shade-1\n  margin: variables.$item-margin\n  padding: .5em 0\n\n  .line\n    border-bottom-style: variables.$border-style\n    border-bottom-width: variables.$border-width\n\n    color: inherit\n    font: inherit\n\n    width: 100%\n\n  .text\n    font: inherit\n    color: inherit\n    background: variables.$background-color\n    padding: variables.$divider-padding\n    line-height: .1em\n"
  },
  {
    "path": "frontend/elements/src/components/wrapper/Clipboard.tsx",
    "content": "import { ComponentChildren, h } from \"preact\";\n\nimport styles from \"./styles.sass\";\nimport { useContext, useState } from \"preact/compat\";\nimport Icon from \"../icons/Icon\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\ntype Props = {\n  text: string;\n  children: ComponentChildren;\n};\n\nconst Clipboard = ({ children, text }: Props) => {\n  const { t } = useContext(TranslateContext);\n  const [isCopied, setIsCopied] = useState(false);\n\n  const copyToClipboard = async (event: Event) => {\n    event.preventDefault();\n    try {\n      await navigator.clipboard.writeText(text);\n      setIsCopied(true);\n      setTimeout(() => setIsCopied(false), 1500); // Reset after 1.5 seconds\n    } catch (err) {\n      console.error(\"Failed to copy: \", err);\n    }\n  };\n\n  return (\n    <section className={styles.clipboardContainer}>\n      <div>{children}&nbsp;</div>\n      <div className={styles.clipboardIcon} onClick={copyToClipboard}>\n        {isCopied ? (\n          <span>- {t(\"labels.copied\")}</span>\n        ) : (\n          <Icon name={\"copy\"} secondary size={13} />\n        )}\n      </div>\n    </section>\n  );\n};\n\nexport default Clipboard;\n"
  },
  {
    "path": "frontend/elements/src/components/wrapper/Container.tsx",
    "content": "import { ComponentChildren, h, HTMLAttributes, JSX } from \"preact\";\nimport { forwardRef, useContext, useEffect } from \"preact/compat\";\n\nimport styles from \"./styles.sass\";\nimport { AppContext } from \"../../contexts/AppProvider\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\ninterface Props extends HTMLAttributes {\n  children: ComponentChildren;\n}\n\nconst Container = forwardRef<HTMLElement>((props: Props, ref)=> {\n  const { lang, hanko, setHanko } = useContext(AppContext);\n  const { setLang } = useContext(TranslateContext);\n\n  useEffect(() => {\n    setLang(lang.replace(/[-]/, \"\"));\n    setHanko((hanko) => {\n      hanko.setLang(lang);\n      return hanko;\n    });\n  }, [hanko, lang, setHanko, setLang]);\n\n  return (\n    <section part={\"container\"} className={styles.container} ref={ref}>\n      {props.children}\n    </section>\n  );\n});\n\nexport default Container;\n"
  },
  {
    "path": "frontend/elements/src/components/wrapper/Content.tsx",
    "content": "import { ComponentChildren } from \"preact\";\n\nimport styles from \"./styles.sass\";\n\ntype Props = {\n  children: ComponentChildren;\n};\n\nconst Content = ({ children }: Props) => {\n  return <section className={styles.content}>{children}</section>;\n};\n\nexport default Content;\n"
  },
  {
    "path": "frontend/elements/src/components/wrapper/Footer.tsx",
    "content": "import { ComponentChildren } from \"preact\";\n\nimport styles from \"./styles.sass\";\n\ninterface Props {\n  hidden?: boolean;\n  children?: ComponentChildren;\n}\n\nconst Footer = ({ children, hidden = false }: Props) => {\n  return !hidden ? (\n    <section className={styles.footer}>{children}</section>\n  ) : null;\n};\n\nexport default Footer;\n"
  },
  {
    "path": "frontend/elements/src/components/wrapper/styles.sass",
    "content": "@use \"../../variables\"\n\n// Container Styles\n\n.container\n  background-color: variables.$background-color\n  padding: variables.$container-padding\n  max-width: variables.$container-max-width\n\n  display: flex\n  flex-direction: column\n  flex-wrap: nowrap\n  justify-content: center\n  align-items: center\n  align-content: flex-start\n  box-sizing: border-box\n\n// Content Styles\n\n.content\n  box-sizing: border-box\n  flex: 0 1 auto\n  width: 100%\n  height: 100%\n\n// Footer Styles\n\n.footer\n  padding: .5rem 0 0\n  box-sizing: border-box\n  width: 100%\n\n  \\:nth-child(1)\n    float: left\n\n  \\:nth-child(2)\n    float: right\n\n// Clipboard Styles\n\n.clipboardContainer\n  display: flex\n\n.clipboardIcon\n  display: flex\n  margin: auto\n  cursor: pointer\n"
  },
  {
    "path": "frontend/elements/src/contexts/AppProvider.tsx",
    "content": "import { JSXInternal } from \"preact/src/jsx\";\nimport { ComponentChildren, createContext, h } from \"preact\";\nimport { TranslateProvider } from \"@denysvuika/preact-translate\";\n\nimport {\n  Dispatch,\n  SetStateAction,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"preact/compat\";\n\nimport {\n  Hanko,\n  HankoError,\n  TechnicalError,\n  State,\n  FlowName,\n  FlowError,\n  LastLogin,\n  StateInitConfig,\n} from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { Translations } from \"../i18n/translations\";\n\nimport Container from \"../components/wrapper/Container\";\nimport InitPage from \"../pages/InitPage\";\nimport LoginInitPage from \"../pages/LoginInitPage\";\nimport PasscodePage from \"../pages/PasscodePage\";\nimport RegisterPasskeyPage from \"../pages/RegisterPasskeyPage\";\nimport LoginPasswordPage from \"../pages/LoginPasswordPage\";\nimport EditPasswordPage from \"../pages/EditPasswordPage\";\nimport LoginMethodChooserPage from \"../pages/LoginMethodChooser\";\nimport RegistrationInitPage from \"../pages/RegistrationInitPage\";\nimport CreatePasswordPage from \"../pages/CreatePasswordPage\";\nimport ProfilePage from \"../pages/ProfilePage\";\nimport ErrorPage from \"../pages/ErrorPage\";\nimport CreateEmailPage from \"../pages/CreateEmailPage\";\nimport CreateUsernamePage from \"../pages/CreateUsernamePage\";\nimport CredentialOnboardingChooserPage from \"../pages/CredentialOnboardingChooser\";\nimport LoginOTPPage from \"../pages/LoginOTPPage\";\nimport LoginSecurityKeyPage from \"../pages/LoginSecurityKeyPage\";\nimport MFAMethodChooserPage from \"../pages/MFAMethodChooserPage\";\nimport CreateOTPSecretPage from \"../pages/CreateOTPSecretPage\";\nimport CreateSecurityKeyPage from \"../pages/CreateSecurityKeyPage\";\nimport DeviceTrustPage from \"../pages/DeviceTrustPage\";\n\nimport SignalLike = JSXInternal.SignalLike;\n\nexport type ComponentName =\n  | \"auth\"\n  | \"login\"\n  | \"registration\"\n  | \"profile\"\n  | \"events\";\n\nexport type HankoAuthMode = \"registration\" | \"login\";\n\nexport interface GlobalOptions {\n  hanko?: Hanko;\n  injectStyles?: boolean;\n  enablePasskeys?: boolean;\n  hidePasskeyButtonOnLogin?: boolean;\n  translations?: Translations;\n  translationsLocation?: string;\n  fallbackLanguage?: string;\n  storageKey?: string;\n}\n\ninterface UIState {\n  username?: string;\n  email?: string;\n  error?: FlowError;\n  isDisabled?: boolean;\n}\n\ninterface Context {\n  hanko: Hanko;\n  setHanko: Dispatch<SetStateAction<Hanko>>;\n  page: h.JSX.Element;\n  setPage: Dispatch<SetStateAction<h.JSX.Element>>;\n  init: (compName: ComponentName) => void;\n  componentName: ComponentName;\n  setComponentName: Dispatch<SetStateAction<ComponentName>>;\n  lang: string;\n  hidePasskeyButtonOnLogin: boolean;\n  prefilledEmail?: string;\n  prefilledUsername?: string;\n  uiState: UIState;\n  setUIState: Dispatch<SetStateAction<UIState>>;\n  initialComponentName: ComponentName;\n  lastLogin?: LastLogin;\n  isOwnFlow: (state: State<any>) => boolean;\n}\n\nexport const AppContext = createContext<Context>(null);\n\ninterface Props {\n  lang?: string | SignalLike<string>;\n  prefilledEmail?: string;\n  prefilledUsername?: string;\n  mode?: HankoAuthMode;\n  nonce?: string;\n  componentName: ComponentName;\n  globalOptions: GlobalOptions;\n  children?: ComponentChildren;\n  createWebauthnAbortSignal: () => AbortSignal;\n}\n\nconst AppProvider = ({\n  lang,\n  prefilledEmail,\n  prefilledUsername,\n  globalOptions,\n  createWebauthnAbortSignal,\n  nonce,\n  ...props\n}: Props) => {\n  const {\n    hanko,\n    injectStyles,\n    hidePasskeyButtonOnLogin,\n    translations,\n    translationsLocation,\n    fallbackLanguage,\n  } = globalOptions;\n\n  // Without this, the initial \"lang\" attribute value sometimes appears to not\n  // be set properly. This results in a wrong X-Language header value being sent\n  // to the API and hence in outgoing emails translated in the wrong language.\n  hanko.setLang(lang?.toString() || fallbackLanguage);\n\n  const ref = useRef<HTMLElement>(null);\n\n  const storageKeyLastLogin = useMemo(\n    () => `${globalOptions.storageKey}_last_login`,\n    [globalOptions.storageKey],\n  );\n\n  const [componentName, setComponentName] = useState<ComponentName>(\n    props.componentName,\n  );\n\n  const [authComponentFlow, setAuthComponentFlow] = useState<FlowName>(\n    props.mode ?? \"login\",\n  );\n\n  // TODO: check if necessary, see also TODO below\n  const hasInitializedRef = useRef(false);\n  const [isReadyToInit, setIsReadyToInit] = useState(false);\n\n  const componentFlowNameMap = useMemo<Record<ComponentName, FlowName>>(\n    () => ({\n      auth: authComponentFlow,\n      login: \"login\",\n      registration: \"registration\",\n      profile: \"profile\",\n      events: null,\n    }),\n    [authComponentFlow],\n  );\n\n  const initComponent = useMemo(() => <InitPage />, []);\n  const [page, setPage] = useState<h.JSX.Element>(initComponent);\n  const [, setHanko] = useState<Hanko>(hanko);\n  const [lastLogin, setLastLogin] = useState<LastLogin>();\n  const [uiState, setUIState] = useState<UIState>({\n    email: prefilledEmail,\n    username: prefilledUsername,\n  });\n\n  const dispatchEvent = function <T>(type: string, detail?: T) {\n    ref.current?.dispatchEvent(\n      new CustomEvent<T>(type, {\n        detail,\n        bubbles: false,\n        composed: true,\n      }),\n    );\n  };\n\n  const isOwnFlow = useCallback(\n    (state: State<any>) =>\n      componentFlowNameMap[componentName] == state.flowName,\n    [componentFlowNameMap, componentName, authComponentFlow],\n  );\n\n  const handleError = (e: any) => {\n    setPage(\n      <ErrorPage error={e instanceof HankoError ? e : new TechnicalError(e)} />,\n    );\n  };\n\n  useMemo(\n    () =>\n      hanko.onBeforeStateChange(({ state }) => {\n        if (!isOwnFlow(state)) {\n          return;\n        }\n\n        setUIState((prev) => ({ ...prev, isDisabled: true, error: undefined }));\n      }),\n    [hanko, isOwnFlow],\n  );\n\n  useEffect(() => {\n    setUIState((prev) => ({\n      ...prev,\n      ...(prefilledEmail && { email: prefilledEmail }),\n      ...(prefilledUsername && { username: prefilledUsername }),\n    }));\n  }, [prefilledEmail, prefilledUsername]);\n\n  useEffect(\n    () =>\n      hanko.onAfterStateChange(async ({ state }) => {\n        if (!isOwnFlow(state)) {\n          return;\n        }\n        if (\n          ![\n            \"onboarding_verify_passkey_attestation\",\n            \"webauthn_credential_verification\",\n            \"login_passkey\",\n            \"thirdparty\",\n          ].includes(state.name)\n        ) {\n          setUIState((prev) => ({ ...prev, isDisabled: false }));\n        }\n\n        switch (state.name) {\n          case \"login_init\":\n            setPage(<LoginInitPage state={state} />);\n            state.passkeyAutofillActivation();\n            break;\n          case \"passcode_confirmation\":\n            setPage(<PasscodePage state={state} />);\n            break;\n          case \"login_otp\":\n            setPage(<LoginOTPPage state={state} />);\n            break;\n          case \"onboarding_create_passkey\":\n            setPage(<RegisterPasskeyPage state={state} />);\n            break;\n          case \"login_password\":\n            setPage(<LoginPasswordPage state={state} />);\n            break;\n          case \"login_password_recovery\":\n            setPage(<EditPasswordPage state={state} />);\n            break;\n          case \"login_security_key\":\n            setPage(<LoginSecurityKeyPage state={state} />);\n            break;\n          case \"mfa_method_chooser\":\n            setPage(<MFAMethodChooserPage state={state} />);\n            break;\n          case \"mfa_otp_secret_creation\":\n            setPage(<CreateOTPSecretPage state={state} />);\n            break;\n          case \"mfa_security_key_creation\":\n            setPage(<CreateSecurityKeyPage state={state} />);\n            break;\n          case \"login_method_chooser\":\n            setPage(<LoginMethodChooserPage state={state} />);\n            break;\n          case \"registration_init\":\n            setPage(<RegistrationInitPage state={state} />);\n            break;\n          case \"password_creation\":\n            setPage(<CreatePasswordPage state={state} />);\n            break;\n          case \"success\":\n            if (state.payload?.last_login) {\n              localStorage.setItem(\n                storageKeyLastLogin,\n                JSON.stringify(state.payload.last_login),\n              );\n            }\n            state.autoStep();\n            break;\n          case \"profile_init\":\n            setPage(\n              <ProfilePage\n                state={state}\n                enablePasskeys={globalOptions.enablePasskeys}\n              />,\n            );\n            break;\n          case \"error\":\n            setPage(<ErrorPage state={state} />);\n            break;\n          case \"onboarding_email\":\n            setPage(<CreateEmailPage state={state} />);\n            break;\n          case \"onboarding_username\":\n            setPage(<CreateUsernamePage state={state} />);\n            break;\n          case \"credential_onboarding_chooser\":\n            setPage(<CredentialOnboardingChooserPage state={state} />);\n            break;\n          case \"device_trust\":\n            setPage(<DeviceTrustPage state={state} />);\n            break;\n        }\n      }),\n    [componentName, componentFlowNameMap],\n  );\n\n  const flowInit = useCallback(async (flowName: FlowName) => {\n    setUIState((prev) => ({ ...prev, isDisabled: true }));\n    const lastLoginEncoded = localStorage.getItem(storageKeyLastLogin);\n    if (lastLoginEncoded) {\n      setLastLogin(JSON.parse(lastLoginEncoded) as LastLogin);\n    }\n    const samlHint = new URLSearchParams(window.location.search).get(\n      \"saml_hint\",\n    );\n    const config: StateInitConfig = {\n      excludeAutoSteps: [\"success\"],\n      cacheKey: `hanko-auth-flow-state`,\n      dispatchAfterStateChangeEvent: false,\n    };\n\n    if (samlHint === \"idp_initiated\") {\n      setAuthComponentFlow(\"token_exchange\");\n      await hanko.createState(\"token_exchange\", {\n        ...config,\n        dispatchAfterStateChangeEvent: true,\n      });\n    } else {\n      const state = await hanko.createState(flowName, config);\n      setAuthComponentFlow(state.flowName);\n      setTimeout(() => state.dispatchAfterStateChangeEvent(), 500);\n    }\n  }, []);\n\n  const init = useCallback(\n    (compName: ComponentName) => {\n      setComponentName(compName);\n      const flowName = componentFlowNameMap[compName];\n\n      if (flowName) {\n        flowInit(flowName).catch(handleError);\n      }\n    },\n    [componentFlowNameMap],\n  );\n\n  // TODO: check if this can be done in cleaner way\n  // Step 1: Set the authComponentFlow from props.mode\n  useEffect(() => {\n    if (!hasInitializedRef.current) {\n      const timer = setTimeout(() => {\n        setAuthComponentFlow(props.mode ?? \"login\");\n        setIsReadyToInit(true);\n      }, 0);\n\n      return () => clearTimeout(timer);\n    }\n  }, [props.mode]);\n\n  // Step 2: Call init after authComponentFlow has been updated\n  useEffect(() => {\n    if (isReadyToInit && !hasInitializedRef.current) {\n      hasInitializedRef.current = true;\n      init(componentName);\n    }\n  }, [isReadyToInit, authComponentFlow, componentName, init]);\n\n  useEffect(() => {\n    const cleanUserDeleted = hanko.onUserDeleted(() => {\n      dispatchEvent(\"onUserDeleted\");\n    });\n\n    const cleanSessionCreated = hanko.onSessionCreated((detail) => {\n      dispatchEvent(\"onSessionCreated\", detail);\n    });\n\n    const cleanSessionExpired = hanko.onSessionExpired(() => {\n      dispatchEvent(\"onSessionExpired\");\n    });\n\n    const cleanUserLoggedOut = hanko.onUserLoggedOut(() => {\n      dispatchEvent(\"onUserLoggedOut\");\n    });\n\n    const cleanBeforeStateChange = hanko.onBeforeStateChange((detail) => {\n      dispatchEvent(\"onBeforeStateChange\", detail);\n    });\n\n    const cleanAfterStateChange = hanko.onAfterStateChange((detail) => {\n      dispatchEvent(\"onAfterStateChange\", detail);\n    });\n\n    return () => {\n      cleanUserDeleted();\n      cleanSessionCreated();\n      cleanSessionExpired();\n      cleanUserLoggedOut();\n      cleanBeforeStateChange();\n      cleanAfterStateChange();\n    };\n  }, [hanko]);\n\n  useEffect(() => {\n    const cb = () => {\n      init(componentName);\n    };\n    if ([\"auth\", \"login\", \"registration\"].includes(componentName)) {\n      const cleanUserLoggedOut = hanko.onUserLoggedOut(cb);\n      const cleanSessionExpired = hanko.onSessionExpired(cb);\n      const cleanUserDeleted = hanko.onUserDeleted(cb);\n      return () => {\n        cleanUserLoggedOut();\n        cleanSessionExpired();\n        cleanUserDeleted();\n      };\n    } else if (componentName === \"profile\") {\n      const cleanSessionCreated = hanko.onSessionCreated(cb);\n      return () => {\n        cleanSessionCreated();\n      };\n    }\n  }, [componentName, hanko, init]);\n\n  // Cast Provider to any to bypass strict JSX return type check (TS2786)\n  // TODO: Find out why, we this need to be casted to any for the build to work.\n  const AppContextProviderAny = AppContext.Provider as any;\n  const TranslateContextProviderAny = TranslateProvider as any;\n  const ContainerAny = Container as any;\n\n  return (\n    <AppContextProviderAny\n      value={{\n        init,\n        initialComponentName: props.componentName,\n        setUIState,\n        uiState,\n        hanko,\n        setHanko,\n        lang: lang?.toString() || fallbackLanguage,\n        prefilledEmail,\n        prefilledUsername,\n        componentName,\n        setComponentName,\n        hidePasskeyButtonOnLogin,\n        page,\n        setPage,\n        lastLogin,\n        isOwnFlow,\n      }}\n    >\n      <TranslateContextProviderAny\n        translations={translations}\n        fallbackLang={fallbackLanguage}\n        root={translationsLocation}\n      >\n        <ContainerAny ref={ref}>\n          {componentName !== \"events\" ? (\n            <>\n              {injectStyles ? (\n                <style\n                  nonce={nonce || undefined}\n                  /* eslint-disable-next-line react/no-danger */\n                  dangerouslySetInnerHTML={{\n                    __html: window._hankoStyle.innerHTML,\n                  }}\n                />\n              ) : null}\n              {page}\n            </>\n          ) : null}\n        </ContainerAny>\n      </TranslateContextProviderAny>\n    </AppContextProviderAny>\n  );\n};\n\nexport default AppProvider;\n"
  },
  {
    "path": "frontend/elements/src/declarations.d.ts",
    "content": "declare module \"*.sass\";\n\n// eslint-disable-next-line no-unused-vars\ninterface Window {\n  _hankoStyle: HTMLStyleElement;\n}\n\ndeclare module \"react\";\n\ndeclare module \"@denysvuika/preact-translate\" {\n  import { Context, h } from \"preact\";\n  import { Dispatch, SetStateAction } from \"preact/compat\";\n\n  interface TranslateParams {\n    [key: string]: string | number;\n  }\n\n  interface LanguageData {\n    [key: string]: any;\n  }\n\n  export const TranslateContext: Context<{\n    lang: string;\n    setLang: Dispatch<SetStateAction<string>>;\n    t: (key: string, params?: TranslateParams) => string;\n    isReady: boolean;\n  }>;\n\n  export interface TranslateProviderProps {\n    root?: string;\n    lang?: string;\n    fallbackLang?: string;\n    translations?: LanguageData;\n    children?: any;\n  }\n  export const TranslateProvider: (\n    props: TranslateProviderProps,\n  ) => h.JSX.Element;\n}\n"
  },
  {
    "path": "frontend/elements/src/example.html",
    "content": "<!--\nThis HTML file showcases the integration of Hanko web components. Here are the features it offers:\n\n  - Hanko Authentication Component: Allows users to authenticate using Hanko. It is represented by the <hanko-auth> tag.\n  - Hanko Profile Component: Displays the user's profile information. It is represented by the <hanko-profile> tag.\n  - Hanko Events Component: Handles various events related to authentication and session management. It is represented\n    by the <hanko-events> tag.\n  - Dialog Element: Presents a dialog to notify the user when a session is over for a certain reason.\n  - Navbar: Includes a logout button and a dropdown menu for selecting the language.\n  - Styling: Provides CSS styles for various elements, including the page, navigation bar, main content, buttons, and\n    the Hanko web components.\n\nTo use this HTML file, you need a running Hanko API instance configured to allow requests from the address under which\nthis file is hosted. Additionally, the API URL needs to be entered in the designated location within the script block.\n\nThe HTML file can be served through any HTTP server. The necessary scripts for the Hanko web components can be imported\neither from a local build or from the CDN.\n\nYou can find more details and functionality explanations within the code comments.\n-->\n<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Hanko Web Component Example</title>\n    <style>\n        /* Page Styling */\n        body {\n            background-color: black;\n            margin: 0;\n            font-weight: 400;\n            font-size: 14px;\n            font-family: sans-serif;\n        }\n\n        /* Navigation Bar Styling */\n        nav {\n            width: 100%;\n            height: 3em;\n            background-color: #01a7c1;\n            display: flex;\n            justify-content: flex-end;\n            align-items: center;\n            gap: 5px;\n            padding: 10px;\n            margin: 0 0 5rem;\n            box-sizing: border-box;\n        }\n\n        /* Main Content Styling */\n        main {\n            display: flex;\n            justify-content: center;\n        }\n\n        /* Main Section Styling */\n        main section {\n            min-width: 400px;\n        }\n\n        /* Button and Select Box Styling */\n        button, select {\n            height: 1.5rem;\n        }\n\n        /* Hanko Web Components Styling */\n        .hankoComponent {\n            /* Adjust the CSS variables related to color to create a dark theme for the web components. */\n            --color: #ccf8ff;\n            --color-shade-1: #01a7c1;\n            --color-shade-2: #546166;\n            --brand-color: #fa00bb;\n            --brand-color-shade-1: #ff2ecb;\n            --brand-contrast-color: white;\n            --link-color: #fac043;\n            --background-color: black;\n            --error-color: #fa0000;\n\n            /* Slightly increase the border radius for elements within the web components. */\n            --border-radius: 5px;\n        }\n\n        .hankoComponent::part(headline1) {\n            /* Adjust CSS properties for the main headlines using the \"::part()\" selector. */\n            font-size: 1.3em;\n            font-weight: 400;\n        }\n\n        #hankoAuth {\n            /* Limit the maximum width and increase the padding of the authentication component. */\n            --container-max-width: 400px;\n            --container-padding: 25px;\n        }\n\n        #hankoAuth::part(headline1) {\n            /* Center the main headlines within the authentication component. */\n            text-align: center;\n        }\n\n        #hankoAuth::part(container) {\n            /* Add a border to the authentication component. */\n            border: solid 1px #546166;\n            border-radius: 15px;\n        }\n\n        #hankoProfile {\n            /* Adjust the profile component to be wider than the authentication component and have less padding. */\n            --container-max-width: 550px;\n            --container-padding: 25px 10px 0;\n        }\n    </style>\n</head>\n<body>\n\n<!-- A dialog element to notify the user the session is over for a certain reason -->\n<dialog id=\"dialog\">\n    <form>\n        <div>\n            <!-- The dialog message will be displayed here -->\n        </div>\n        <br>\n        <button formmethod=\"dialog\">Back to Login</button>\n    </form>\n</dialog>\n\n<!-- Navbar -->\n<nav>\n    <!-- Button for logging out -->\n    <button id=\"logoutButton\" hidden>Log out</button>\n\n    <!-- Dropdown for selecting the language -->\n    <select id=\"langSelect\">\n        <option value=\"bn\">Bengali</option>\n        <option value=\"de\">German</option>\n        <option value=\"en\" selected>English</option>\n        <option value=\"fr\">French</option>\n        <option value=\"it\">Italian</option>\n        <option value=\"nl\">Dutch</option>\n        <option value=\"pt-BR\">Brazilian Portuguese</option>\n        <option value=\"zh\">Chinese</option>\n    </select>\n</nav>\n\n<!-- Main Content -->\n<main>\n    <section>\n        <!-- Hanko Authentication Component -->\n        <hanko-auth  id=\"hankoAuth\" class=\"hankoComponent\" lang=\"en\"></hanko-auth>\n\n        <!-- Hanko Profile Component -->\n        <hanko-profile id=\"hankoProfile\" class=\"hankoComponent\" lang=\"en\" hidden></hanko-profile>\n\n        <!-- Hanko Events Component -->\n        <hanko-events id=\"hankoEvents\" class=\"hankoComponent\"></hanko-events>\n    </section>\n</main>\n\n<!--\nThe following script allows either the authentication component or the profile component to be displayed. When the\nsession expires, a dialog appears to inform the user and provides a way to return to the authentication component.\n\nHere's a breakdown of what the script does:\n\n1. Imports the necessary modules for Hanko Elements. You can choose to import from a local build or from the CDN by\n   uncommenting the appropriate lines.\n2. Registers the Hanko Web Components using the provided API URL and translations.\n3. Retrieves DOM elements from the HTML file for further manipulation.\n4. Adds event listeners for \"onSessionCreated,\" \"onAuthFlowCompleted,\" \"onSessionExpired,\" \"onUserLoggedOut,\" and\n   \"onUserDeleted\" events, enabling control over which elements to display or hide based on the event triggers.\n5. Adds a click event listener to the logout button to trigger the user logout process.\n6. Adds a close event listener to the dialog element to show the authentication component once the dialog is closed.\n7. Adds an input event listener to the language select element to update the language of the Hanko components.\n8. Defines an initialization function that adds the event listeners, checks for a valid session, and determines whether\n   to display the authentication or the profile component based on the session's validity.\n9. Calls the initialization function to set up the integration.\n\nPlease note that the \"onAuthFlowCompleted\" event should be handled in your own implementation (to customize the behavior\nafter the user completes the authentication flow), while other events are optional to handle.\n-->\n<script type=\"module\">\n    // Uncomment the following two lines if you want to import the local build, which can be created by changing to\n    // the \"/frontend\" directory and running \"npm run build:elements\".\n    //\n    import {register} from \"../dist/elements.js\";\n    import {all} from \"../dist/i18n/all.js\";\n\n    // Comment the following two lines if you don't want to import Hanko Elements from the CDN, see comment above.\n    // import {register} from \"https://cdn.jsdelivr.net/npm/@teamhanko/hanko-elements/dist/elements.js\";\n    // import {all} from \"https://cdn.jsdelivr.net/npm/@teamhanko/hanko-elements/dist/i18n/all.js\";\n\n    // Change `apiUrl` to the URL where your Hanko-API instance is running.\n    const apiUrl = \"http://localhost:8000\";\n\n    // Register Hanko Web Components\n    const {hanko} = await register(apiUrl, {translations: all, sessionCheckInterval: 5000});\n\n    // Get DOM elements\n    const hankoAuthEl = document.getElementById(\"hankoAuth\"),\n        hankoProfileEl = document.getElementById(\"hankoProfile\"),\n        hankoEventsEl = document.getElementById(\"hankoEvents\"),\n        logoutButtonEl = document.getElementById(\"logoutButton\"),\n        langSelectEl = document.getElementById(\"langSelect\"),\n        dialogEl = document.getElementById(\"dialog\"),\n        dialogContentEl = dialogEl.getElementsByTagName(\"div\").item(0);\n\n    // Function to show or hide individual elements\n    function setVisibility(element, visible) {\n        element.hidden = !visible;\n    }\n\n    // Function to show the authentication component or the profile component\n    function showAuthComponent(showAuth) {\n        setVisibility(hankoAuthEl, showAuth);\n        setVisibility(hankoProfileEl, !showAuth);\n    }\n\n    // Function to show the dialog element\n    function showDialog(message) {\n        dialogContentEl.innerText = message;\n        dialogEl.showModal();\n    }\n\n    // Function to change the language of the Hanko web components\n    function selectLanguage(lang) {\n        hankoAuthEl.lang = lang;\n        hankoProfileEl.lang = lang;\n    }\n\n    // Function to add event listeners\n    function addEventListeners() {\n        hankoEventsEl.addEventListener(\"onSessionCreated\", () => {\n            // The user has completed the authentication flow through the hanko-auth component, so we can display the\n            // hanko-profile and hide the hanko-auth component.\n            showAuthComponent(false); // Show profile component\n            setVisibility(logoutButtonEl, true); // Show the logout button\n\n            // When the dialog was initially opened due to the session expiring in the past, and it has not been closed\n            // manually before re-authentication taking place in another browser window, close the dialog automatically.\n            if (dialogEl.open) {\n                dialogEl.close(); // Close the dialog when it is still open and a new session was created\n            }\n        });\n\n        hankoEventsEl.addEventListener(\"onSessionExpired\", () => {\n            // The session has expired, so we can show the dialog to notify the user. Additionally, the logout button\n            // can be hidden.\n            showDialog(\"Your session has expired\"); // Show message on the overlay dialog\n            setVisibility(logoutButtonEl, false); // Hide the logout button\n        });\n\n        hankoEventsEl.addEventListener(\"onUserLoggedOut\", () => {\n            // The user has logged out, so we show the hanko-auth component and hide the profile element as well as the\n            // logout button.\n            showAuthComponent(true); // Show authentication component\n            setVisibility(logoutButtonEl, false);\n        });\n\n        hankoEventsEl.addEventListener(\"onUserDeleted\", () => {\n            // The user has deleted the account, so we only show the dialog and hide the other elements.\n            setVisibility(hankoProfileEl, false);\n            setVisibility(logoutButtonEl, false);\n            showDialog(\"Your account has been deleted\");\n        });\n\n        logoutButtonEl.addEventListener(\"click\", () => {\n            // Logout the user, triggering the `onUserLoggedOut` event upon success.\n            hanko.logout();\n        });\n\n        dialogEl.addEventListener('close', async () => {\n            // When the dialog, which informs the user that the session has expired, is closed, check if the session\n            // has been created again, for example, in another browser window. If it has, there is no need to switch to\n            // the hanko-auth component.\n            const { is_valid } = await hanko.validateSession();\n            if (!is_valid) {\n                showAuthComponent(true); // Show authentication component\n            }\n        });\n\n        langSelectEl.addEventListener(\"input\", (event) => {\n            // If the user selects a different language from the language select box, update the language of the Hanko\n            // components.\n            selectLanguage(event.target.value); // The value is either \"de\", \"en\" or \"fr\"\n        });\n    }\n\n    // Initialization function\n    async function init() {\n        addEventListeners();\n\n        // Check if a valid session exists\n        const { is_valid } = await hanko.validateSession();\n        if (is_valid) {\n            showAuthComponent(false); // Show profile component\n            setVisibility(logoutButtonEl, true); // Show the logout button\n        }\n\n        // If the session is not valid, we don't need to do anything since the authentication component is already\n        // visible and the profile component, as well as the logout button, have been hidden in the HTML code using the\n        // \"hidden\" attribute.\n    }\n\n    // Call the initialization function\n    await init();\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "frontend/elements/src/hooks/UseFlowEffects.ts",
    "content": "import { Dispatch, SetStateAction, useContext, useEffect } from \"preact/compat\";\nimport { Action } from \"@teamhanko/hanko-frontend-sdk\";\nimport { AppContext } from \"../contexts/AppProvider\";\n\nexport const useFlowEffects = (\n  flowAction: Action<any> | undefined,\n  setIsLoading: Dispatch<SetStateAction<boolean>>,\n  setIsSuccess: Dispatch<SetStateAction<boolean>>,\n) => {\n  const { hanko, setUIState, isOwnFlow } = useContext(AppContext);\n\n  useEffect(\n    () =>\n      hanko.onBeforeStateChange(({ state }) => {\n        if (!flowAction || !isOwnFlow(state)) {\n          return;\n        }\n\n        setUIState((prev) => ({ ...prev, isDisabled: true, error: undefined }));\n        setIsLoading(state.invokedAction.name == flowAction.name);\n      }),\n    [flowAction, hanko, isOwnFlow, setIsLoading, setUIState],\n  );\n\n  useEffect(\n    () =>\n      hanko.onAfterStateChange(({ state }) => {\n        if (!flowAction || !isOwnFlow(state)) {\n          return;\n        }\n        setIsSuccess(state.previousAction?.name == flowAction.name);\n        setIsLoading(false);\n      }),\n    [hanko, setIsSuccess, setIsLoading, flowAction, isOwnFlow],\n  );\n};\n"
  },
  {
    "path": "frontend/elements/src/hooks/UseFlowState.ts",
    "content": "import { State, StateName } from \"@teamhanko/hanko-frontend-sdk\";\nimport { useEffect, useState } from \"preact/compat\";\n\nexport const useFlowState = <T extends StateName>(\n  initialFlowState: State<T>,\n) => {\n  const [flowState, setFlowState] = useState<State<T>>(initialFlowState);\n\n  useEffect(() => {\n    if (initialFlowState) {\n      setFlowState(initialFlowState);\n    }\n  }, [initialFlowState]);\n\n  return { flowState };\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/all.ts",
    "content": "import { Translations } from \"./translations\";\nimport { bn } from \"./bn\";\nimport { de } from \"./de\";\nimport { en } from \"./en\";\nimport { fr } from \"./fr\";\nimport { it } from \"./it\";\nimport { nl } from \"./nl\";\nimport { ptBR } from \"./pt-BR\";\nimport { zh } from \"./zh\";\nexport const all: Translations = { bn, de, en, fr, it, nl, ptBR, zh };\n"
  },
  {
    "path": "frontend/elements/src/i18n/bn.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const bn: Translation = {\n  headlines: {\n    error: \"একটি ত্রুটি উৎপন্ন হয়েছে\",\n    loginEmail: \"সাইন ইন করুন অথবা নিবন্ধন করুন\",\n    loginEmailNoSignup: \"সাইন ইন\",\n    loginFinished: \"সফল লগইন\",\n    loginPasscode: \"পাসকোড দিন\",\n    loginPassword: \"পাসওয়ার্ড লিখুন\",\n    registerAuthenticator: \"একটি পাসকি তৈরি করুন\",\n    registerConfirm: \"অ্যাকাউন্ট তৈরি করুন\",\n    registerPassword: \"নতুন পাসওয়ার্ড সেট করুন\",\n    otpSetUp: \"প্রমাণীকরণ অ্যাপ সেট আপ করুন\",\n    profileEmails: \"ইমেইল\",\n    profilePassword: \"পাসওয়ার্ড\",\n    profilePasskeys: \"পাসকি\",\n    isPrimaryEmail: \"প্রাথমিক ইমেইল\",\n    setPrimaryEmail: \"প্রাথমিক ইমেল সেট করুন\",\n    createEmail: \"নতুন ইমেইল লিখুন\",\n    createUsername: \"নতুন ব্যবহারকারী নাম লিখুন\",\n    emailVerified: \"যাচাইকৃত \",\n    emailUnverified: \"অযাচাইকৃত\",\n    emailDelete: \"মুছে ফেলা\",\n    renamePasskey: \"পাসকির নাম পরিবর্তন করুন\",\n    deletePasskey: \"পাসকি মুছুন\",\n    lastUsedAt: \"শেষবার ব্যবহার করা হয়েছে\",\n    createdAt: \"এ নির্মিত\",\n    connectedAccounts: \"সংযুক্ত অ্যাকাউন্ট\",\n    deleteAccount: \"অ্যাকাউন্ট মুছে ফেলুন\",\n    accountNotFound: \"অ্যাকাউন্ট পাওয়া যাচ্ছে না\",\n    signIn: \"সাইন ইন\",\n    signUp: \"নিবন্ধন করুন\",\n    selectLoginMethod: \"লগইন পদ্ধতি নির্বাচন করুন\",\n    setupLoginMethod: \"লগইন পদ্ধতি সেটআপ করুন\",\n    lastUsed: \"সর্বশেষ দেখা হয়েছে\",\n    ipAddress: \"আইপি ঠিকানা\",\n    revokeSession: \"সেশন বাতিল করুন\",\n    profileSessions: \"সেশন\",\n    mfaSetUp: \"MFA সেট আপ করুন\",\n    securityKeySetUp: \"সিকিউরিটি কি যোগ করুন\",\n    securityKeyLogin: \"সিকিউরিটি কি\",\n    otpLogin: \"প্রমাণীকরণ কোড\",\n    renameSecurityKey: \"সিকিউরিটি কি এর নামকরণ করুন\",\n    deleteSecurityKey: \"সিকিউরিটি কি মুছুন\",\n    securityKeys: \"সিকিউরিটি কিগুলি\",\n    authenticatorApp: \"প্রমাণীকরণ অ্যাপ\",\n    authenticatorAppAlreadySetUp: \"প্রমাণীকরণ অ্যাপ সেট আপ করা হয়েছে\",\n    authenticatorAppNotSetUp: \"প্রমাণীকরণ অ্যাপ সেট আপ করুন\",\n    trustDevice: \"এই ব্রাউজারটি বিশ্বাস করবেন?\",\n    deleteIdentity: \"সংযোগ মুছুন\",\n  },\n  texts: {\n    enterPasscode: \"আপনার ইমেইল ঠিকানায় পাঠানো পাসকোডটি প্রবেশ করান।\",\n    enterPasscodeNoEmail:\n      \"আপনার প্রাথমিক ইমেল ঠিকানায় প্রেরিত পাসকোডটি লিখুন।\",\n    setupPasskey:\n      \"একটি পাসকি দিয়ে সহজে এবং নিরাপদে আপনার অ্যাকাউন্টে সাইন ইন করুন। দ্রষ্টব্য: আপনার বায়োমেট্রিক ডেটা শুধুমাত্র আপনার ডিভাইসে সংরক্ষিত থাকে এবং কখনই কারো সাথে শেয়ার করা হবে না।\",\n    createAccount:\n      '\"{emailAddress}\"-এর জন্য কোনো অ্যাকাউন্ট বিদ্যমান নেই। আপনি একটি নতুন অ্যাকাউন্ট তৈরি করতে চান?',\n    otpEnterVerificationCode:\n      \"আপনার প্রমাণীকরণ অ্যাপ থেকে প্রাপ্ত একবারের পাসওয়ার্ড (OTP) নিচে লিখুন:\",\n    otpScanQRCode:\n      \"আপনার প্রমাণীকরণ অ্যাপ দিয়ে QR কোড স্ক্যান করুন (যেমন Google Authenticator বা অন্য কোনো TOTP অ্যাপ)। বিকল্পভাবে, আপনি অ্যাপে OTP গোপন কী ম্যানুয়ালি লিখতে পারেন।\",\n    otpSecretKey: \"OTP গোপন কী\",\n    passwordFormatHint: \"{minLength} এবং {maxLength} অক্ষরের মধ্যে হতে হবে।\",\n    setPrimaryEmail:\n      \"এই ইমেইল ঠিকানা আপনার সাথে যোগাযোগ করার জন্য নির্ধারণ করুন।\",\n    isPrimaryEmail:\n      \"প্রয়োজন হলে এই ইমেল ঠিকানা ব্যবহার করা হবে আপনার সাথে যোগাযোগ করার জন্য।\",\n    emailVerified: \"এই ইমেল ঠিকানা যাচাই করা হয়েছে.\",\n    emailUnverified: \"এই ইমেল ঠিকানা যাচাই করা হয়নি.\",\n    emailDelete:\n      \"আপনি এই ইমেল ঠিকানা মুছে ফেললে, এটি আর সাইন ইন করতে ব্যবহার করা যাবে না.\",\n    renamePasskey: \"পাসকির জন্য একটি নাম সেট করুন।\",\n    deletePasskey: \"আপনার অ্যাকাউন্ট থেকে এই পাসকি মুছুন.\",\n    deleteAccount:\n      \"আপনি কি এই অ্যাকাউন্টটি মুছে ফেলার বিষয়ে নিশ্চিত? সমস্ত ডেটা অবিলম্বে মুছে ফেলা হবে এবং পুনরুদ্ধার করা যাবে না।\",\n    noAccountExists: '\"{emailAddress}\"-এর জন্য কোনো অ্যাকাউন্ট বিদ্যমান নেই।',\n    selectLoginMethodForFutureLogins:\n      \"ভবিষ্যতে লগইনের জন্য একটি প্রয়োজনীয় লগইন পদ্ধতি নির্বাচন করুন।\",\n    howDoYouWantToLogin: \"আপনি কিভাবে লগইন করতে চান?\",\n    mfaSetUp:\n      \"মাল্টি-ফ্যাক্টর প্রমাণীকরণের (MFA) মাধ্যমে আপনার অ্যাকাউন্ট সুরক্ষিত করুন। MFA আপনার লগইন প্রক্রিয়ায় একটি অতিরিক্ত পদক্ষেপ যোগ করে, নিশ্চিত করে যে আপনার পাসওয়ার্ড বা ইমেইল অ্যাকাউন্ট ক্ষতিগ্রস্ত হলে আপনার অ্যাকাউন্ট সুরক্ষিত থাকে।\",\n    securityKeyLogin:\n      \"আপনার নিরাপত্তা কী সংযুক্ত বা সক্রিয় করুন, তারপর নীচের বোতামটিতে ক্লিক করুন। প্রস্তুত হলে, USB, NFC, আপনার মোবাইল ফোনের মাধ্যমে এটি ব্যবহার করুন। লগইন প্রক্রিয়া সম্পূর্ণ করতে নির্দেশাবলীর অনুসরণ করুন।\",\n    otpLogin:\n      \"এককালীন পাসওয়ার্ড (OTP) প্রাপ্ত করতে আপনার প্রমাণীকরণ অ্যাপ খুলুন। আপনার লগইন সম্পূর্ণ করতে নীচের ক্ষেত্রে কোড প্রবেশ করুন।\",\n    renameSecurityKey: \"নিরাপত্তা কী জন্য একটি নাম সেট করুন।\",\n    deleteSecurityKey: \"এই নিরাপত্তা কীটি আপনার অ্যাকাউন্ট থেকে মুছে ফেলুন।\",\n    authenticatorAppAlreadySetUp:\n      \"আপনার অ্যাকাউন্ট একটি প্রমাণীকরণ অ্যাপ দ্বারা সুরক্ষিত যা মাল্টি-ফ্যাক্টর প্রমাণীকরণের জন্য সময়-ভিত্তিক এককালীন পাসওয়ার্ড (TOTP) তৈরি করে।\",\n    authenticatorAppNotSetUp:\n      \"মাল্টি-ফ্যাক্টর প্রমাণীকরণের জন্য সময়-ভিত্তিক এককালীন পাসওয়ার্ড (TOTP) তৈরি করার জন্য একটি প্রমাণীকরণ অ্যাপ দ্বারা আপনার অ্যাকাউন্টটি সুরক্ষিত করুন।\",\n    securityKeySetUp: \"নিরাপত্তা কী যুক্ত করুন\",\n    trustDevice:\n      \"যদি আপনি এই ব্রাউজারটিকে বিশ্বাস করেন, তবে পরেরবার লগইন করার সময় আপনাকে আপনার OTP (ওয়ান-টাইম-পাসওয়ার্ড) প্রবেশ করাতে হবে না বা মাল্টি-ফ্যাক্টর অথেনটিকেশন (MFA) এর জন্য আপনার সিকিউরিটি কী ব্যবহার করতে হবে না।\",\n  },\n  labels: {\n    or: \"বা\",\n    no: \"না\",\n    yes: \"হ্যাঁ\",\n    email: \"ইমেইল\",\n    continue: \"চালিয়ে যান\",\n    copied: \"নকল করা হয়েছে\",\n    skip: \"এড়িয়ে যান\",\n    save: \"সংরক্ষণ\",\n    passkey: \"পাসওয়ার্ড\",\n    passcode: \"পাসকোড\",\n    password: \"পাসওয়ার্ড\",\n    signInPassword: \"একটি পাসওয়ার্ড দিয়ে সাইন ইন\",\n    signInPasscode: \"একটি পাসকোড দিয়ে সাইন ইন করুন\",\n    forgotYourPassword: \"আপনি কি পাসওয়ার্ড ভুলে গেছেন?\",\n    back: \"পেছনে\",\n    signInPasskey: \"একটি পাসকি দিয়ে সাইন ইন করুন\",\n    registerAuthenticator: \"একটি পাসকি তৈরি করুন\",\n    signIn: \"সাইন ইন করুন\",\n    signUp: \"সাইন আপ\",\n    sendNewPasscode: \"নতুন কোড পাঠান\",\n    passwordRetryAfter: \"{passwordRetryAfter} এ আবার চেষ্টা করুন\",\n    passcodeResendAfter: \"{passcodeResendAfter} এ একটি নতুন কোডের অনুরোধ করুন\",\n    unverifiedEmail: \"অযাচাইকৃত\",\n    primaryEmail: \"প্রাথমিক\",\n    setAsPrimaryEmail: \"প্রাথমিক হিসাবে সেট করুন\",\n    verify: \"যাচাই করুন\",\n    delete: \"মুছুন\",\n    newEmailAddress: \"নতুন ইমেইল ঠিকানা\",\n    newPassword: \"নতুন পাসওয়ার্ড\",\n    rename: \"নাম পরিবর্তন করুন\",\n    newPasskeyName: \"নতুন পাসকি নাম\",\n    addEmail: \"ইমেল যোগ করুন\",\n    createPasskey: \"একটি পাসকি তৈরি করুন\",\n    webauthnUnsupported: \"পাসকিগুলি আপনার ব্রাউজার দ্বারা সমর্থিত নয়৷\",\n    signInWith: \"{provider} দিয়ে সাইন ইন করুন\",\n    deleteAccount: \"হ্যাঁ, এই অ্যাকাউন্টটি মুছুন.\",\n    emailOrUsername: \"ইমেল বা ব্যবহারকারীর নাম\",\n    username: \"ব্যবহারকারীর নাম\",\n    optional: \"ঐচ্ছিক\",\n    dontHaveAnAccount: \"একটি অ্যাকাউন্ট নেই?\",\n    alreadyHaveAnAccount: \"ইতিমধ্যে অ্যাকাউন্ট আছে?\",\n    changeUsername: \"ব্যবহারকারীর নাম পরিবর্তন করুন\",\n    setUsername: \"ব্যবহারকারীর নাম সেট করুন\",\n    changePassword: \"পাসওয়ার্ড পরিবর্তন করুন\",\n    setPassword: \"পাসওয়ার্ড সেট করুন\",\n    revoke: \"বাতিল করুন\",\n    currentSession: \"বর্তমান সেশন\",\n    authenticatorApp: \"প্রমাণীকরণ অ্যাপ\",\n    securityKey: \"নিরাপত্তা কী\",\n    securityKeyUse: \"নিরাপত্তা কী ব্যবহার করুন\",\n    newSecurityKeyName: \"নতুন নিরাপত্তা কী নাম\",\n    createSecurityKey: \"নিরাপত্তা কী যোগ করুন\",\n    authenticatorAppManage: \"প্রমাণীকরণ অ্যাপ পরিচালনা করুন\",\n    authenticatorAppAdd: \"সেট আপ করুন\",\n    configured: \"কনফিগার করা হয়েছে\",\n    useAnotherMethod: \"আরেকটি পদ্ধতি ব্যবহার করুন\",\n    lastUsed: \"সর্বশেষ ব্যবহৃত\",\n    trustDevice: \"এই ব্রাউজারটি বিশ্বাস করবেন\",\n    staySignedIn: \"সাইন ইন থাকা চালিয়ে যান\",\n    connectAccount: \"অ্যাকাউন্ট সংযুক্ত করুন\",\n  },\n  errors: {\n    somethingWentWrong:\n      \"একটি প্রযুক্তিগত ত্রুটি ঘটেছে. অনুগ্রহ করে একটু পরে আবার চেষ্টা করুন.\",\n    requestTimeout: \"অনুরোধ সময় শেষ হয়েছে.\",\n    invalidPassword: \"ভুল ইমেইল বা পাসওয়ার্ড।\",\n    invalidPasscode: \"দেওয়া পাসকোড সঠিক ছিল না।\",\n    passcodeAttemptsReached:\n      \"পাসকোডটি অনেকবার ভুলভাবে প্রবেশ করানো হয়েছে৷ একটি নতুন কোড অনুরোধ করুন.\",\n    tooManyRequests:\n      \"অনেক অনুরোধ করা হয়েছে. অনুগ্রহ করে অনুরোধকৃত অপারেশন পুনরাবৃত্তি করার জন্য অপেক্ষা করুন।\",\n    unauthorized:\n      \"আপনার সেশনের মেয়াদ শেষ হয়ে গেছে। অনুগ্রহ পূর্বক আরো একবার প্রবেশ করুন.\",\n    invalidWebauthnCredential: \"এই পাসকি আর ব্যবহার করা যাবে না।\",\n    passcodeExpired: \"পাসকোড মেয়াদ শেষ হয়েছে. একটি নতুন অনুরোধ করুন.\",\n    userVerification:\n      \"ব্যবহারকারী যাচাইকরণ প্রয়োজন. অনুগ্রহ করে নিশ্চিত করুন যে আপনার প্রমাণীকরণকারী ডিভাইসটি একটি পিন বা বায়োমেট্রিক দ্বারা সুরক্ষিত।\",\n    emailAddressAlreadyExistsError: \"ইমেল ঠিকানাটি ইতিমধ্যেই বিদ্যমান।\",\n    maxNumOfEmailAddressesReached: \"আর কোন ইমেল ঠিকানা যোগ করা যাবে না.\",\n    thirdPartyAccessDenied:\n      \"অ্যাক্সেস অস্বীকার করা হয়েছে৷ অনুরোধটি ব্যবহারকারীর দ্বারা বাতিল করা হয়েছে বা প্রদানকারী অন্যান্য কারণে অ্যাক্সেস অস্বীকার করেছে৷\",\n    thirdPartyMultipleAccounts:\n      \"অ্যাকাউন্ট শনাক্ত করতে পারে না। ইমেল ঠিকানা একাধিক অ্যাকাউন্ট দ্বারা ব্যবহৃত হয়।\",\n    thirdPartyUnverifiedEmail:\n      \"ইমেল যাচাইকরণ প্রয়োজন. অনুগ্রহ করে আপনার প্রদানকারীর সাথে ব্যবহৃত ইমেল ঠিকানা যাচাই করুন।\",\n    signupDisabled: \"অ্যাকাউন্ট নিবন্ধন নিষ্ক্রিয় করা হয়েছে\",\n    handlerNotFoundError: \"কার্যকরী পদক্ষেপ পাওয়া যায়নি\",\n  },\n  flowErrors: {\n    technical_error:\n      \"একটি প্রযুক্তিগত ত্রুটি ঘটেছে. অনুগ্রহ করে একটু পরে আবার চেষ্টা করুন.\",\n    flow_expired_error:\n      \"সেশনটি মেয়াদ উত্তীর্ণ হয়েছে, দয়া করে পুনরায় শুরু করতে বাটনটি ক্লিক করুন।\",\n    value_invalid_error: \"প্রবেশিত মান অবৈধ।\",\n    passcode_invalid: \"প্রদত্ত পাসকোড সঠিক নয়।\",\n    passkey_invalid: \"এই পাসকী আর ব্যবহার করা যাবে না\",\n    passcode_max_attempts_reached:\n      \"পাসকোডটি অনেকবার ভুল ভাবে প্রবেশ করা হয়েছে। দয়া করে নতুন কোড অনুরোধ করুন।\",\n    rate_limit_exceeded:\n      \"অনেকগুলি অনুরোধ করা হয়েছে। অনুরোধকৃত অপারেশন পুনরায় প্রয়াত করতে অপেক্ষা করুন।\",\n    unknown_username_error: \"ব্যবহারকারীর নাম অজানা।\",\n    unknown_email_error: \"ইমেইল ঠিকানাটি অজানা।\",\n    username_already_exists: \"ব্যবহারকারীর নাম ইতিমধ্যে নেওয়া হয়েছে।\",\n    invalid_username_error:\n      \"ব্যবহারকারীর নাম শুধুমাত্র অক্ষর, সংখ্যা এবং আন্ডারস্কোর থাকতে পারে।\",\n    email_already_exists: \"ইমেলটি ইতিমধ্যে নেওয়া হয়েছে।\",\n    not_found: \"অনুরোধ করা রিসোর্সটি পাওয়া যায়নি।\",\n    operation_not_permitted_error: \"অপারেশন অনুমোদিত নয়।\",\n    flow_discontinuity_error:\n      \"ব্যবহারকারীর সেটিংস বা প্রদানকারীর কনফিগারেশনের কারণে প্রক্রিয়াটি চালিয়ে যাওয়া সম্ভব নয়।\",\n    form_data_invalid_error: \"জমা দেওয়া ফর্ম ডেটাতে ত্রুটি রয়েছে।\",\n    unauthorized:\n      \"আপনার সেশনের মেয়াদ শেষ হয়ে গেছে। অনুগ্রহ পূর্বক আরো একবার প্রবেশ করুন.\",\n    value_missing_error: \"মান অনুপস্থিত।\",\n    value_too_long_error: \"মানটি খুব দীর্ঘ।\",\n    value_too_short_error: \"মানটি খুব ছোট।\",\n    webauthn_credential_invalid_mfa_only:\n      \"এই প্রমাণপত্রটি কেবল দ্বিতীয় ফ্যাক্টর নিরাপত্তা কী হিসাবে ব্যবহার করা যেতে পারে।\",\n    webauthn_credential_already_exists:\n      \"অনুরোধটি সময়সীমা শেষ হয়ে গেছে, বাতিল হয়েছে অথবা ডিভাইসটি ইতিমধ্যে নিবন্ধিত। পুনরায় চেষ্টা করুন অথবা অন্য একটি ডিভাইস ব্যবহার করে চেষ্টা করুন।\",\n    platform_authenticator_required:\n      \"আপনার অ্যাকাউন্ট প্ল্যাটফর্ম প্রমাণীকরণকারীদের ব্যবহার করতে কনফিগার করা হয়েছে, তবে আপনার বর্তমান ডিভাইস বা ব্রাউজার এই বৈশিষ্ট্য সমর্থন করে না। দয়া করে একটি সামঞ্জস্যপূর্ণ ডিভাইস বা ব্রাউজার ব্যবহার করে আবার চেষ্টা করুন।\",\n    third_party_access_denied:\n      \"অ্যাক্সেস অস্বীকার করা হয়েছে৷ অনুরোধটি ব্যবহারকারীর দ্বারা বাতিল করা হয়েছে বা প্রদানকারী অন্যান্য কারণে অ্যাক্সেস অস্বীকার করেছে৷\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/de.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const de: Translation = {\n  headlines: {\n    error: \"Ein Fehler ist aufgetreten\",\n    loginEmail: \"Anmelden / Registrieren\",\n    loginEmailNoSignup: \"Anmelden\",\n    loginFinished: \"Login erfolgreich\",\n    loginPasscode: \"Passcode eingeben\",\n    loginPassword: \"Passwort eingeben\",\n    registerAuthenticator: \"Erstellen Sie einen Passkey\",\n    registerConfirm: \"Konto erstellen?\",\n    registerPassword: \"Neues Passwort eingeben\",\n    otpSetUp: \"Authenticator-App einrichten\",\n    profileEmails: \"E-Mails\",\n    profilePassword: \"Passwort\",\n    profilePasskeys: \"Passkeys\",\n    isPrimaryEmail: \"Primäre E-Mail-Adresse\",\n    setPrimaryEmail: \"Als primäre E-Mail-Adresse festlegen\",\n    createEmail: \"Geben Sie eine neue E-Mail-Adresse ein\",\n    createUsername: \"Geben Sie einen neuen Benutzernamen ein\",\n    emailVerified: \"Verifiziert\",\n    emailUnverified: \"Unverifiziert\",\n    emailDelete: \"Löschen\",\n    renamePasskey: \"Passkey umbenennen\",\n    deletePasskey: \"Passkey löschen\",\n    lastUsedAt: \"Zuletzt benutzt am\",\n    createdAt: \"Erstellt am\",\n    connectedAccounts: \"Verbundene Konten\",\n    deleteAccount: \"Konto löschen\",\n    accountNotFound: \"Konto nicht gefunden\",\n    signIn: \"Anmelden\",\n    signUp: \"Registrieren\",\n    selectLoginMethod: \"Wähle die Anmelde-Methode\",\n    setupLoginMethod: \"Anmelde-Methode einrichten\",\n    lastUsed: \"Zuletzt gesehen\",\n    ipAddress: \"IP Adresse\",\n    revokeSession: \"Sitzung beenden\",\n    profileSessions: \"Sitzungen\",\n    mfaSetUp: \"MFA einrichten\",\n    securityKeySetUp: \"Sicherheitsschlüssel hinzufügen\",\n    securityKeyLogin: \"Sicherheitsschlüssel\",\n    otpLogin: \"Authentifizierungscode\",\n    renameSecurityKey: \"Sicherheitsschlüssel umbenennen\",\n    deleteSecurityKey: \"Sicherheitsschlüssel löschen\",\n    securityKeys: \"Sicherheitsschlüssel\",\n    authenticatorApp: \"Authenticator-App\",\n    authenticatorAppNotSetUp: \"Authenticator-App einrichten\",\n    authenticatorAppAlreadySetUp: \"Authenticator-App ist eingerichtet\",\n    trustDevice: \"Diesem Browser vertrauen?\",\n    deleteIdentity: \"Verbindung löschen\",\n  },\n  texts: {\n    enterPasscode:\n      \"Geben Sie den Passcode ein, der an Ihre E-Mail-Adresse gesendet wurde.\",\n    enterPasscodeNoEmail:\n      \"Geben Sie den Passcode ein, der an Ihre primäre E-Mail-Adresse gesendet wurde.\",\n    setupPasskey:\n      \"Ihr Gerät unterstützt die sichere Anmeldung mit Passkeys. Hinweis: Ihre biometrischen Daten verbleiben sicher auf Ihrem Gerät und werden niemals an unseren Server gesendet.\",\n    createAccount:\n      'Es existiert kein Konto für \"{emailAddress}\". Möchten Sie ein neues Konto erstellen?',\n    otpEnterVerificationCode:\n      \"Geben Sie den einmaligen Passwort (OTP) ein, den Sie von Ihrer Authenticator-App erhalten haben:\",\n    otpScanQRCode:\n      \"Scannen Sie den QR-Code mit Ihrer Authenticator-App (z.B. Google Authenticator oder jede andere TOTP-App). Alternativ können Sie den OTP-Geheimschlüssel manuell in die App eingeben.\",\n    otpSecretKey: \"OTP-Geheimschlüssel\",\n    passwordFormatHint:\n      \"Das Passwort muss zwischen {minLength} und {maxLength} Zeichen lang sein.\",\n    setPrimaryEmail: \"Setzen Sie diese E-Mail-Adresse als Kontaktadresse.\",\n    isPrimaryEmail:\n      \"Diese E-Mail-Adresse wird verwendet, um Sie bei Bedarf zu kontaktieren.\",\n    emailVerified: \"Diese E-Mail-Adresse wurde verifiziert.\",\n    emailUnverified: \"Diese E-Mail-Adresse wurde noch nicht verifiziert.\",\n    emailDelete:\n      \"Wenn Sie diese E-Mail-Adresse löschen, kann sie nicht mehr für die Anmeldung bei Ihrem Konto verwendet werden. Passkeys, die möglicherweise mit dieser E-Mail-Adresse erstellt wurden, funktionieren weiterhin.\",\n    renamePasskey:\n      \"Legen Sie einen Namen für den Passkey fest, anhand dessen Sie erkennen können, wo er gespeichert ist.\",\n    deletePasskey:\n      \"Löschen Sie diesen Passkey aus Ihrem Konto. Beachten Sie, dass der Passkey noch auf Ihren Geräten vorhanden ist und auch dort gelöscht werden muss.\",\n    deleteAccount:\n      \"Sind Sie sicher, dass Sie Ihr Konto löschen wollen? Alle Daten werden sofort gelöscht und können nicht wiederhergestellt werden.\",\n    noAccountExists: 'Es existiert kein Konto für \"{emailAddress}\".',\n    selectLoginMethodForFutureLogins:\n      \"Wählen Sie eine der folgenden Anmelde-Methoden aus, um sie für zukünftige Anmeldungen zu verwenden.\",\n    howDoYouWantToLogin: \"Wie möchten Sie sich anmelden?\",\n    mfaSetUp:\n      \"Schützen Sie Ihr Konto mit Mehrfaktor-Authentifizierung (MFA). MFA fügt Ihrer Anmeldeprozedur einen zusätzlichen Schritt hinzu, um sicherzustellen, dass Ihr Konto geschützt bleibt, selbst wenn Ihr Passwort oder E-Mail-Konto kompromittiert wird.\",\n    securityKeyLogin:\n      \"Verbinden oder aktivieren Sie Ihren Sicherheitsschlüssel und klicken Sie dann auf die Schaltfläche unten. Wenn Sie bereit sind, verwenden Sie USB, NFC oder Ihr Mobilgerät. Befolgen Sie die Anweisungen, um den Anmeldevorgang abzuschließen.\",\n    otpLogin:\n      \"Öffnen Sie Ihre Authenticator-App, um den einmaligen Passwort (OTP) zu erhalten. Geben Sie den Code im untenstehenden Feld ein, um sich anzumelden.\",\n    renameSecurityKey:\n      \"Legen Sie einen Namen für den Sicherheitsschlüssel fest.\",\n    deleteSecurityKey:\n      \"Löschen Sie diesen Sicherheitsschlüssel aus Ihrem Konto.\",\n    authenticatorAppAlreadySetUp:\n      \"Ihr Konto ist durch eine Authenticator-App geschützt, die zeitbasierte einmalige Passwörter (TOTP) für die Mehrfaktor-Authentifizierung generiert.\",\n    authenticatorAppNotSetUp:\n      \"Schützen Sie Ihr Konto mit einer Authenticator-App, die zeitbasierte einmalige Passwörter (TOTP) für die Mehrfaktor-Authentifizierung generiert.\",\n    securityKeySetUp:\n      \"Verwenden Sie einen dedizierten Sicherheitsschlüssel über USB, Bluetooth oder NFC oder Ihr Mobiltelefon. Schließen Sie Ihren Sicherheitsschlüssel an oder aktivieren Sie ihn, und klicken Sie dann auf die Schaltfläche unten und folgen Sie den Anweisungen, um die Registrierung abzuschließen.\",\n    trustDevice:\n      \"Wenn Sie diesem Browser vertrauen, müssen Sie bei der nächsten Anmeldung weder Ihr OTP (Einmalpasswort) eingeben noch Ihren Sicherheitsschlüssel für die Multi-Faktor-Authentifizierung (MFA) verwenden.\",\n  },\n  labels: {\n    or: \"oder\",\n    no: \"nein\",\n    yes: \"ja\",\n    email: \"E-Mail\",\n    continue: \"Weiter\",\n    copied: \"kopiert\",\n    skip: \"Überspringen\",\n    save: \"Speichern\",\n    password: \"Passwort\",\n    passkey: \"Passwort\",\n    passcode: \"Passcode\",\n    signInPassword: \"Mit einem Passwort anmelden\",\n    signInPasscode: \"Mit einem Passcode anmelden\",\n    forgotYourPassword: \"Passwort vergessen?\",\n    back: \"Zurück\",\n    signInPasskey: \"Anmelden mit Passkey\",\n    registerAuthenticator: \"Erstellen Sie einen Passkey\",\n    signIn: \"Anmelden\",\n    signUp: \"Registrieren\",\n    sendNewPasscode: \"Neuen Code senden\",\n    passwordRetryAfter: \"Neuer Versuch in {passwordRetryAfter}\",\n    passcodeResendAfter: \"Neuen Code in {passcodeResendAfter} anfordern\",\n    unverifiedEmail: \"unverifiziert\",\n    primaryEmail: \"primär\",\n    setAsPrimaryEmail: \"Als primär festlegen\",\n    verify: \"Verifizieren\",\n    delete: \"Löschen\",\n    newEmailAddress: \"Neue E-Mail-Adresse\",\n    newPassword: \"Neues Passwort\",\n    rename: \"Umbenennen\",\n    newPasskeyName: \"Neuer Passkey Name\",\n    addEmail: \"E-Mail-Adresse hinzufügen\",\n    createPasskey: \"Erstellen Sie einen Passkey\",\n    webauthnUnsupported: \"Passkeys werden von ihrem Browser nicht unterstützt\",\n    signInWith: \"Anmelden mit {provider}\",\n    deleteAccount: \"Ja, dieses Konto löschen.\",\n    emailOrUsername: \"E-Mail oder Nutzername\",\n    username: \"Nutzername\",\n    optional: \"optional\",\n    dontHaveAnAccount: \"Sie haben noch kein Konto?\",\n    alreadyHaveAnAccount: \"Haben Sie bereits ein Konto?\",\n    changeUsername: \"Benutzernamen ändern\",\n    setUsername: \"Benutzernamen setzen\",\n    changePassword: \"Passwort ändern\",\n    setPassword: \"Passwort setzen\",\n    revoke: \"Beenden\",\n    currentSession: \"Aktuelle Sitzung\",\n    authenticatorApp: \"Authentifizierungs-App\",\n    securityKey: \"Sicherheitsschlüssel\",\n    securityKeyUse: \"Sicherheitsschlüssel verwenden\",\n    newSecurityKeyName: \"Neuer Sicherheitsschlüsselname\",\n    createSecurityKey: \"Sicherheitsschlüssel hinzufügen\",\n    authenticatorAppManage: \"Authentifizierungs-App verwalten\",\n    authenticatorAppAdd: \"Einrichten\",\n    configured: \"konfiguriert\",\n    useAnotherMethod: \"Eine andere Methode verwenden\",\n    lastUsed: \"Zuletzt verwendet\",\n    trustDevice: \"Diesem Browser vertrauen\",\n    staySignedIn: \"Angemeldet bleiben\",\n    connectAccount: \"Verbinde Konto\",\n  },\n  errors: {\n    somethingWentWrong:\n      \"Ein technischer Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.\",\n    requestTimeout: \"Die Anfrage hat das Zeitlimit überschritten.\",\n    invalidPassword: \"E-Mail-Adresse oder Passwort falsch.\",\n    invalidPasscode: \"Der Passcode war nicht richtig.\",\n    passcodeAttemptsReached:\n      \"Der Passcode wurde zu oft falsch eingegeben. Bitte fragen Sie einen neuen Code an.\",\n    tooManyRequests:\n      \"Es wurden zu viele Anfragen gestellt. Bitte warten Sie, um den gewünschten Vorgang zu wiederholen.\",\n    unauthorized:\n      \"Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.\",\n    invalidWebauthnCredential:\n      \"Dieser Passkey kann nicht mehr verwendet werden.\",\n    passcodeExpired:\n      \"Der Passcode ist abgelaufen. Bitte fordern Sie einen neuen Code an.\",\n    userVerification:\n      \"Nutzer-Verifikation erforderlich. Bitte stellen Sie sicher, dass Ihr Gerät durch eine PIN oder Biometrie abgesichert ist.\",\n    emailAddressAlreadyExistsError: \"Die E-Mail-Adresse existiert bereits.\",\n    maxNumOfEmailAddressesReached:\n      \"Es können keine weiteren E-Mail-Adressen hinzugefügt werden.\",\n    thirdPartyAccessDenied:\n      \"Zugriff verweigert. Die Anfrage wurde durch den Nutzer abgebrochen oder der Provider hat den Zugriff aus anderen Gründen verweigert.\",\n    thirdPartyMultipleAccounts:\n      \"Konto kann nicht eindeutig identifiziert werden. Die genutzte E-Mail-Adresse wird von mehreren Konten verwendet.\",\n    thirdPartyUnverifiedEmail:\n      \"Verifizierung der E-Mail-Adresse erforderlich. Bitte verifizieren sie die genutzte E-Mail-Adresse bei ihrem Provider.\",\n    signupDisabled: \"Die Kontoregistrierung ist deaktiviert.\",\n    handlerNotFoundError:\n      \"Der aktuelle Schritt in Ihrem Prozess wird von dieser Anwendungsversion nicht unterstützt.\",\n  },\n  flowErrors: {\n    technical_error:\n      \"Ein technischer Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.\",\n    flow_expired_error:\n      \"Die Sitzung ist abgelaufen, bitte klicken Sie auf die Schaltfläche, um neu zu starten.\",\n    value_invalid_error: \"Der eingegebene Wert ist ungültig.\",\n    passcode_invalid: \"Der angegebene Passcode war nicht korrekt.\",\n    passkey_invalid: \"Dieser Passkey kann nicht mehr verwendet werden.\",\n    passcode_max_attempts_reached:\n      \"Der Passcode wurde zu oft falsch eingegeben. Bitte fordern Sie einen neuen Code an.\",\n    rate_limit_exceeded:\n      \"Zu viele Anfragen wurden gestellt. Bitte warten Sie, um die angeforderte Operation zu wiederholen.\",\n    unknown_username_error: \"Der Benutzername ist unbekannt.\",\n    unknown_email_error: \"Die Email Adresse ist unbekannt.\",\n    username_already_exists: \"Der Benutzername ist bereits vergeben.\",\n    invalid_username_error:\n      \"Der Benutzername darf nur Buchstaben, Zahlen und Unterstriche enthalten.\",\n    email_already_exists: \"Die E-Mail-Adresse ist bereits vergeben.\",\n    not_found: \"Die angeforderte Ressource wurde nicht gefunden.\",\n    operation_not_permitted_error: \"Der Vorgang ist nicht erlaubt.\",\n    flow_discontinuity_error:\n      \"Der Prozess kann aufgrund der Nutzereinstellungen oder der Konfiguration des Anbieters nicht fortgesetzt werden.\",\n    form_data_invalid_error:\n      \"Die übermittelten Formulardaten enthalten Fehler.\",\n    unauthorized:\n      \"Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.\",\n    value_missing_error: \"Der Wert fehlt.\",\n    value_too_long_error: \"Der Wert ist zu lang.\",\n    value_too_short_error: \"Der Wert ist zu kurz.\",\n    webauthn_credential_invalid_mfa_only:\n      \"Diese Anmeldeinformation kann nur als zweite Sicherheitsfaktor verwendet werden.\",\n    webauthn_credential_already_exists:\n      \"Die Anfrage wurde entweder abgebrochen, abgelaufen oder das Gerät ist bereits registriert. Bitte versuchen Sie es erneut oder verwenden Sie ein anderes Gerät.\",\n    platform_authenticator_required:\n      \"Ihr Konto ist so konfiguriert, dass es Plattform-Authentifikatoren verwendet, jedoch unterstützt Ihr aktuelles Gerät oder Ihr Browser diese Funktion nicht. Bitte versuchen Sie es mit einem kompatiblen Gerät oder Browser erneut.\",\n    third_party_access_denied:\n      \"Zugriff verweigert. Die Anfrage wurde durch den Nutzer abgebrochen oder der Provider hat den Zugriff aus anderen Gründen verweigert.\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/en.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const en: Translation = {\n  headlines: {\n    error: \"An error has occurred\",\n    loginEmail: \"Sign in or create account\",\n    loginEmailNoSignup: \"Sign in\",\n    loginFinished: \"Login successful\",\n    loginPasscode: \"Enter passcode\",\n    loginPassword: \"Enter password\",\n    registerAuthenticator: \"Create a passkey\",\n    registerConfirm: \"Create account?\",\n    registerPassword: \"Set new password\",\n    otpSetUp: \"Set up authenticator app\",\n    profileEmails: \"Emails\",\n    profilePassword: \"Password\",\n    profilePasskeys: \"Passkeys\",\n    isPrimaryEmail: \"Primary email address\",\n    setPrimaryEmail: \"Set primary email address\",\n    createEmail: \"Enter a new email\",\n    createUsername: \"Enter a new username\",\n    emailVerified: \"Verified\",\n    emailUnverified: \"Unverified\",\n    emailDelete: \"Delete\",\n    renamePasskey: \"Rename passkey\",\n    deletePasskey: \"Delete passkey\",\n    lastUsedAt: \"Last used at\",\n    createdAt: \"Created at\",\n    connectedAccounts: \"Connected accounts\",\n    deleteAccount: \"Delete account\",\n    accountNotFound: \"Account not found\",\n    signIn: \"Sign in\",\n    signUp: \"Create account\",\n    selectLoginMethod: \"Select login method\",\n    setupLoginMethod: \"Set up login method\",\n    lastUsed: \"Last seen\",\n    ipAddress: \"IP address\",\n    revokeSession: \"Revoke session\",\n    profileSessions: \"Sessions\",\n    mfaSetUp: \"Set up MFA\",\n    securityKeySetUp: \"Add security key\",\n    securityKeyLogin: \"Security key\",\n    otpLogin: \"Authentication code\",\n    renameSecurityKey: \"Rename security key\",\n    deleteSecurityKey: \"Delete security key\",\n    securityKeys: \"Security keys\",\n    authenticatorApp: \"Authenticator app\",\n    authenticatorAppAlreadySetUp: \"Authenticator app is set up\",\n    authenticatorAppNotSetUp: \"Set up authenticator app\",\n    trustDevice: \"Trust this browser?\",\n    deleteIdentity: \"Delete connection\",\n  },\n  texts: {\n    enterPasscode: \"Enter the passcode sent to your email address.\",\n    enterPasscodeNoEmail:\n      \"Enter the passcode that was sent to your primary email address.\",\n    setupPasskey:\n      \"Sign in to your account easily and securely with a passkey. Note: Your biometric data is only stored on your devices and will never be shared with anyone.\",\n    createAccount:\n      'No account exists for \"{emailAddress}\". Do you want to create a new account?',\n    otpEnterVerificationCode:\n      \"Enter the one-time password (OTP) obtained from your authenticator app below:\",\n    otpScanQRCode:\n      \"Scan the QR code using your authenticator app (such as Google Authenticator or any other TOTP app). Alternatively, you can manually enter the OTP secret key into the app.\",\n    otpSecretKey: \"OTP secret key\",\n    passwordFormatHint:\n      \"Must be between {minLength} and {maxLength} characters long.\",\n    securityKeySetUp:\n      \"Use a dedicated security key via USB, Bluetooth, or NFC, or your mobile phone. Connect or activate your security key, then click the button below and follow the prompts to complete the registration.\",\n    setPrimaryEmail: \"Set this email address to be used for contacting you.\",\n    isPrimaryEmail:\n      \"This email address will be used to contact you if necessary.\",\n    emailVerified: \"This email address has been verified.\",\n    emailUnverified: \"This email address has not been verified.\",\n    emailDelete:\n      \"If you delete this email address, it can no longer be used to sign in.\",\n    renamePasskey: \"Set a name for the passkey.\",\n    deletePasskey: \"Delete this passkey from your account.\",\n    deleteAccount:\n      \"Are you sure you want to delete this account? All data will be deleted immediately and cannot be recovered.\",\n    noAccountExists: 'No account exists for \"{emailAddress}\".',\n    selectLoginMethodForFutureLogins:\n      \"Select one of the following login methods to use for future logins.\",\n    howDoYouWantToLogin: \"How do you want to login?\",\n    mfaSetUp:\n      \"Protect your account with Multi-Factor Authentication (MFA). MFA adds an additional step to your login process, ensuring that even if your password or email account is compromised, your account stays secure.\",\n    securityKeyLogin:\n      \"Connect or activate your security key, then click the button below. Once ready, use it via USB, NFC, your mobile phone. Follow the prompts to complete the login process.\",\n    otpLogin:\n      \"Open your authenticator app to obtain the one-time password (OTP). Enter the code in the field below to complete your login.\",\n    renameSecurityKey: \"Set a name for the security key.\",\n    deleteSecurityKey: \"Delete this security key from your account.\",\n    authenticatorAppAlreadySetUp:\n      \"Your account is secured with an authenticator app that generates time-based one-time passwords (TOTP) for multi-factor authentication.\",\n    authenticatorAppNotSetUp:\n      \"Secure your account with an authenticator app that generates time-based one-time passwords (TOTP) for multi-factor authentication.\",\n    trustDevice:\n      \"If you trust this browser, you won’t need to enter your OTP (One-Time-Password) or use your security key for multi-factor authentication (MFA) the next time you log in.\",\n  },\n  labels: {\n    or: \"or\",\n    no: \"no\",\n    yes: \"yes\",\n    email: \"Email\",\n    continue: \"Continue\",\n    copied: \"copied\",\n    skip: \"Skip\",\n    save: \"Save\",\n    password: \"Password\",\n    passkey: \"Passkey\",\n    passcode: \"Passcode\",\n    signInPassword: \"Sign in with a password\",\n    signInPasscode: \"Sign in with a passcode\",\n    forgotYourPassword: \"Forgot your password?\",\n    back: \"Back\",\n    signInPasskey: \"Sign in with a passkey\",\n    registerAuthenticator: \"Create a passkey\",\n    signIn: \"Sign in\",\n    signUp: \"Create account\",\n    sendNewPasscode: \"Send new code\",\n    passwordRetryAfter: \"Retry in {passwordRetryAfter}\",\n    passcodeResendAfter: \"Request a new code in {passcodeResendAfter}\",\n    unverifiedEmail: \"unverified\",\n    primaryEmail: \"primary\",\n    setAsPrimaryEmail: \"Set as primary\",\n    verify: \"Verify\",\n    delete: \"Delete\",\n    newEmailAddress: \"New email address\",\n    newPassword: \"New password\",\n    rename: \"Rename\",\n    newPasskeyName: \"New passkey name\",\n    addEmail: \"Add email\",\n    createPasskey: \"Create a passkey\",\n    webauthnUnsupported: \"Passkeys are not supported by your browser\",\n    signInWith: \"Continue with {provider}\",\n    deleteAccount: \"Yes, delete this account.\",\n    emailOrUsername: \"Email or username\",\n    username: \"Username\",\n    optional: \"optional\",\n    dontHaveAnAccount: \"Don't have an account?\",\n    alreadyHaveAnAccount: \"Already have an account?\",\n    changeUsername: \"Change username\",\n    setUsername: \"Set username\",\n    changePassword: \"Change password\",\n    setPassword: \"Set password\",\n    revoke: \"Revoke\",\n    currentSession: \"Current session\",\n    authenticatorApp: \"Authenticator app\",\n    securityKey: \"Security key\",\n    securityKeyUse: \"Use security key\",\n    newSecurityKeyName: \"New security key name\",\n    createSecurityKey: \"Add a security key\",\n    authenticatorAppManage: \"Manage authenticator app\",\n    authenticatorAppAdd: \"Set up\",\n    configured: \"configured\",\n    useAnotherMethod: \"Use another method\",\n    lastUsed: \"Last used\",\n    trustDevice: \"Trust this browser\",\n    staySignedIn: \"Stay signed in\",\n    connectAccount: \"Connect account\",\n  },\n  errors: {\n    somethingWentWrong:\n      \"A technical error has occurred. Please try again later.\",\n    requestTimeout: \"The request timed out.\",\n    invalidPassword: \"Wrong email or password.\",\n    invalidPasscode: \"The passcode provided was not correct.\",\n    passcodeAttemptsReached:\n      \"The passcode was entered incorrectly too many times. Please request a new code.\",\n    tooManyRequests:\n      \"Too many requests have been made. Please wait to repeat the requested operation.\",\n    unauthorized: \"Your session has expired. Please log in again.\",\n    invalidWebauthnCredential: \"This passkey cannot be used anymore.\",\n    passcodeExpired: \"The passcode has expired. Please request a new one.\",\n    userVerification:\n      \"User verification required. Please ensure your authenticator device is protected with a PIN or biometric.\",\n    emailAddressAlreadyExistsError: \"The email address already exists.\",\n    maxNumOfEmailAddressesReached: \"No further email addresses can be added.\",\n    thirdPartyAccessDenied:\n      \"Access denied. The request was cancelled by the user or the provider has denied access for other reasons.\",\n    thirdPartyMultipleAccounts:\n      \"Cannot identify account. The email address is used by multiple accounts.\",\n    thirdPartyUnverifiedEmail:\n      \"Email verification required. Please verify the used email address with your provider.\",\n    signupDisabled: \"Account registration is disabled.\",\n    handlerNotFoundError:\n      \"The current step in your process is not supported by this application version. Please try again later or contact support if the issue persists.\",\n  },\n  flowErrors: {\n    technical_error: \"A technical error has occurred. Please try again later.\",\n    flow_expired_error:\n      \"The session has expired, please click the button to restart.\",\n    value_invalid_error: \"The entered value is invalid.\",\n    passcode_invalid: \"The passcode provided was not correct.\",\n    passkey_invalid: \"This passkey cannot be used anymore.\",\n    passcode_max_attempts_reached:\n      \"The passcode was entered incorrectly too many times. Please request a new code.\",\n    rate_limit_exceeded:\n      \"Too many requests have been made. Please wait to repeat the requested operation.\",\n    unknown_username_error: \"The username is unknown.\",\n    unknown_email_error: \"The email address is unknown.\",\n    username_already_exists: \"The username is already taken.\",\n    invalid_username_error:\n      \"The username must contain only letters, numbers, and underscores.\",\n    email_already_exists: \"The email is already taken.\",\n    not_found: \"The requested resource was not found.\",\n    operation_not_permitted_error: \"The operation is not permitted.\",\n    flow_discontinuity_error:\n      \"The process cannot be continued due to user settings or the provider's configuration.\",\n    form_data_invalid_error: \"The submitted form data contains errors.\",\n    unauthorized: \"Your session has expired. Please log in again.\",\n    value_missing_error: \"The value is missing.\",\n    value_too_long_error: \"Value is too long.\",\n    value_too_short_error: \"The value is too short.\",\n    webauthn_credential_invalid_mfa_only:\n      \"This credential can be used as a second factor security key only.\",\n    webauthn_credential_already_exists:\n      \"The request either timed out, was canceled or the device is already registered. Please try again or try using another device.\",\n    platform_authenticator_required:\n      \"Your account is configured to use platform authenticators, but your current device or browser does not support this feature. Please try again with a compatible device or browser.\",\n    third_party_access_denied:\n      \"Access denied. The request was cancelled by the user or the provider has denied access for other reasons.\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/fr.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const fr: Translation = {\n  headlines: {\n    error: \"Une erreur s'est produite\",\n    loginEmail: \"Se connecter ou s'inscrire\",\n    loginEmailNoSignup: \"Se connecter\",\n    loginFinished: \"Connexion réussie\",\n    loginPasscode: \"Entrez le code d'accès\",\n    loginPassword: \"Entrez le mot de passe\",\n    registerAuthenticator: \"Créer une clé d'identification\",\n    registerConfirm: \"Créer un compte ?\",\n    registerPassword: \"Définir un nouveau mot de passe\",\n    otpSetUp: \"Configurer l'application d'authentification\",\n    profileEmails: \"Adresses e-mail\",\n    profilePassword: \"Mot de passe\",\n    profilePasskeys: \"Clés d'identification\",\n    isPrimaryEmail: \"Adresse e-mail principale\",\n    setPrimaryEmail: \"Définir l'adresse e-mail principale\",\n    createEmail: \"Entrer un nouvel e-mail\",\n    createUsername: \"Entrer un nouveau nom d'utilisateur\",\n    emailVerified: \"Vérifié\",\n    emailUnverified: \"Non vérifié\",\n    emailDelete: \"Supprimer\",\n    renamePasskey: \"Renommer la clé d'identification\",\n    deletePasskey: \"Supprimer la clé d'identification\",\n    lastUsedAt: \"Dernière utilisation le\",\n    createdAt: \"Créé le\",\n    connectedAccounts: \"Comptes connectés\",\n    deleteAccount: \"Supprimer le compte\",\n    accountNotFound: \"Compte non trouvé\",\n    signIn: \"Se connecter\",\n    signUp: \"S'inscrire\",\n    selectLoginMethod: \"Sélectionner la méthode de connexion\",\n    setupLoginMethod: \"Configurer la méthode de connexion\",\n    lastUsed: \"Dernière vue\",\n    ipAddress: \"Adresse IP\",\n    revokeSession: \"Révoquer la session\",\n    profileSessions: \"Sessions\",\n    mfaSetUp: \"Configurer MFA\",\n    securityKeySetUp: \"Ajouter une clé de sécurité\",\n    securityKeyLogin: \"Clé de sécurité\",\n    otpLogin: \"Code d'authentification\",\n    renameSecurityKey: \"Renommer la clé de sécurité\",\n    deleteSecurityKey: \"Supprimer la clé de sécurité\",\n    securityKeys: \"Clés de sécurité\",\n    authenticatorApp: \"Application d'authentification\",\n    authenticatorAppAlreadySetUp:\n      \"L'application d'authentification est configurée\",\n    authenticatorAppNotSetUp: \"Configurer l'application d'authentification\",\n    trustDevice: \"Faire confiance à ce navigateur ?\",\n    deleteIdentity: \"Supprimer la connexion\",\n  },\n  texts: {\n    enterPasscode: \"Saisissez le code d'accès envoyé à votre adresse e-mail.\",\n    enterPasscodeNoEmail:\n      \"Entrez le code envoyé à votre adresse e-mail principale.\",\n    setupPasskey:\n      \"Connectez-vous à votre compte facilement et en toute sécurité avec une clé d'identification. Remarque : Vos données biométriques sont uniquement stockées sur vos appareils et ne seront jamais partagées avec qui que ce soit.\",\n    createAccount:\n      'Aucun compte n\\'existe pour \"{emailAddress}\". Voulez-vous créer un nouveau compte ?',\n    otpEnterVerificationCode:\n      \"Entrez le mot de passe à usage unique (OTP) obtenu à partir de votre application d'authentification ci-dessous :\",\n    otpScanQRCode:\n      \"Scannez le code QR en utilisant votre application d'authentification (comme Google Authenticator ou toute autre application TOTP). Alternativement, vous pouvez entrer manuellement la clé secrète OTP dans l'application.\",\n    otpSecretKey: \"Clé secrète OTP\",\n    passwordFormatHint:\n      \"Doit contenir entre {minLength} et {maxLength} caractères.\",\n    setPrimaryEmail: \"Définir cette adresse e-mail comme adresse de contact.\",\n    isPrimaryEmail:\n      \"Cette adresse e-mail sera utilisée pour vous contacter si nécessaire.\",\n    emailVerified: \"Cette adresse e-mail a été vérifiée.\",\n    emailUnverified: \"Cette adresse e-mail n'a pas été vérifiée.\",\n    emailDelete:\n      \"Si vous supprimez cette adresse e-mail, elle ne pourra plus être utilisée pour vous connecter à votre compte. Les clés d'identification qui ont pu être créées avec cette adresse e-mail resteront intactes.\",\n    renamePasskey:\n      \"Définissez un nom pour la clé d'identification qui vous aide à identifier où elle est stockée.\",\n    deletePasskey:\n      \"Supprimez cette clé d'identification de votre compte. Notez que la clé d'identification continuera d'exister sur vos appareils et devra également y être supprimée.\",\n    deleteAccount:\n      \"Êtes-vous sûr de vouloir supprimer ce compte ? Toutes les données seront supprimées immédiatement et ne pourront pas être récupérées.\",\n    noAccountExists: 'Aucun compte n\\'existe pour \"{emailAddress}\".',\n    selectLoginMethodForFutureLogins:\n      \"Sélectionnez l'une des méthodes de connexion suivantes à utiliser pour les connexions futures.\",\n    howDoYouWantToLogin: \"Comment souhaitez-vous vous connecter ?\",\n    mfaSetUp:\n      \"Protégez votre compte avec l'authentification à plusieurs facteurs (MFA). La MFA ajoute une étape supplémentaire à votre processus de connexion, garantissant que même si votre mot de passe ou votre compte e-mail est compromis, votre compte reste sécurisé.\",\n    securityKeyLogin:\n      \"Connectez ou activez votre clé de sécurité, puis cliquez sur le bouton ci-dessous. Une fois prêt, utilisez-le via USB, NFC ou votre téléphone mobile. Suivez les instructions pour terminer le processus de connexion.\",\n    otpLogin:\n      \"Ouvrez votre application d'authentification pour obtenir le mot de passe à usage unique (OTP). Entrez le code dans le champ ci-dessous pour terminer votre connexion.\",\n    renameSecurityKey: \"Définissez un nom pour la clé de sécurité.\",\n    deleteSecurityKey: \"Supprimez cette clé de sécurité de votre compte.\",\n    authenticatorAppAlreadySetUp:\n      \"Votre compte est sécurisé avec une application d'authentification qui génère des mots de passe à usage unique basés sur le temps (TOTP) pour l'authentification à plusieurs facteurs.\",\n    authenticatorAppNotSetUp:\n      \"Sécurisez votre compte avec une application d'authentification qui génère des mots de passe à usage unique basés sur le temps (TOTP) pour l'authentification à plusieurs facteurs.\",\n    securityKeySetUp:\n      \"Utilisez une clé de sécurité dédiée via USB, Bluetooth ou NFC, ou votre téléphone mobile. Connectez ou activez votre clé de sécurité, puis cliquez sur le bouton ci-dessous et suivez les instructions pour terminer l'enregistrement.\",\n    trustDevice:\n      \"Si vous faites confiance à ce navigateur, vous n'aurez pas besoin de saisir votre OTP (mot de passe à usage unique) ou d'utiliser votre clé de sécurité pour l'authentification à plusieurs facteurs (MFA) lors de votre prochaine connexion.\",\n  },\n  labels: {\n    or: \"ou\",\n    no: \"non\",\n    yes: \"oui\",\n    email: \"E-mail\",\n    continue: \"Continuer\",\n    copied: \"copié\",\n    skip: \"Passer\",\n    save: \"Enregistrer\",\n    password: \"Mot de passe\",\n    passkey: \"Clé d'identification\",\n    passcode: \"Code d'accès\",\n    signInPassword: \"Se connecter avec un mot de passe\",\n    signInPasscode: \"Se connecter avec un code d'accès\",\n    forgotYourPassword: \"Mot de passe oublié ?\",\n    back: \"Retour\",\n    signInPasskey: \"Se connecter avec une clé d'identification\",\n    registerAuthenticator: \"Créer une clé d'identification\",\n    signIn: \"Se connecter\",\n    signUp: \"S'inscrire\",\n    sendNewPasscode: \"Envoyer un nouveau code\",\n    passwordRetryAfter: \"Réessayez dans {passwordRetryAfter}\",\n    passcodeResendAfter: \"Demander un nouveau code dans {passcodeResendAfter}\",\n    unverifiedEmail: \"non vérifiée\",\n    primaryEmail: \"principale\",\n    setAsPrimaryEmail: \"Définir comme principale\",\n    verify: \"Vérifier\",\n    delete: \"Supprimer\",\n    newEmailAddress: \"Nouvelle adresse e-mail\",\n    newPassword: \"Nouveau mot de passe\",\n    rename: \"Renommer\",\n    newPasskeyName: \"Nouveau nom de clé d'identification\",\n    addEmail: \"Ajouter une adresse e-mail\",\n    createPasskey: \"Créer une clé d'identification\",\n    webauthnUnsupported:\n      \"Les clés d'identification ne sont pas prises en charge par votre navigateur\",\n    signInWith: \"Se connecter avec {provider}\",\n    deleteAccount: \"Oui, supprimer ce compte.\",\n    emailOrUsername: \"E-mail ou Nom d'utilisateur\",\n    username: \"Nom d'utilisateur\",\n    optional: \"facultatif\",\n    dontHaveAnAccount: \"Vous n'avez pas de compte ?\",\n    alreadyHaveAnAccount: \"Vous avez déjà un compte ?\",\n    changeUsername: \"Changer le nom d'utilisateur\",\n    setUsername: \"Définir le nom d'utilisateur\",\n    changePassword: \"Changer le mot de passe\",\n    setPassword: \"Définir le mot de passe\",\n    revoke: \"Révoquer\",\n    currentSession: \"Session en cours\",\n    authenticatorApp: \"Application d'authentification\",\n    securityKey: \"Clé de sécurité\",\n    securityKeyUse: \"Utiliser la clé de sécurité\",\n    newSecurityKeyName: \"Nouveau nom de clé de sécurité\",\n    createSecurityKey: \"Ajouter une clé de sécurité\",\n    authenticatorAppManage: \"Gérer l'application d'authentification\",\n    authenticatorAppAdd: \"Configurer\",\n    configured: \"configuré\",\n    useAnotherMethod: \"Utiliser une autre méthode\",\n    lastUsed: \"Dernière utilisation\",\n    trustDevice: \"Faire confiance à ce navigateur\",\n    staySignedIn: \"Rester connecté\",\n    connectAccount: \"Connecter un compte\",\n  },\n  errors: {\n    somethingWentWrong:\n      \"Une erreur technique s'est produite. Veuillez réessayer ultérieurement.\",\n    requestTimeout: \"La demande a expiré.\",\n    invalidPassword: \"Mauvais e-mail ou mot de passe.\",\n    invalidPasscode: \"Le code d'accès fourni n'était pas correct.\",\n    passcodeAttemptsReached:\n      \"Le code d'accès a été saisi incorrectement trop de fois. Veuillez demander un nouveau code.\",\n    tooManyRequests:\n      \"Trop de demandes ont été effectuées. Veuillez attendre pour répéter l'opération demandée.\",\n    unauthorized: \"Votre session a expiré. Veuillez vous connecter à nouveau.\",\n    invalidWebauthnCredential:\n      \"Cette clé d'identification ne peut plus être utilisée.\",\n    passcodeExpired:\n      \"Le code d'accès a expiré. Veuillez demander un nouveau code.\",\n    userVerification:\n      \"Vérification de l'utilisateur requise. Veuillez vous assurer que votre appareil d'authentification est protégé par un code PIN ou une biométrie.\",\n    emailAddressAlreadyExistsError: \"L'adresse e-mail existe déjà.\",\n    maxNumOfEmailAddressesReached:\n      \"Aucune autre adresse e-mail ne peut être ajoutée.\",\n    thirdPartyAccessDenied:\n      \"Accès refusé. La demande a été annulée par l'utilisateur ou le fournisseur a refusé l'accès pour d'autres raisons.\",\n    thirdPartyMultipleAccounts:\n      \"Impossible d'identifier le compte. L'adresse e-mail est utilisée par plusieurs comptes.\",\n    thirdPartyUnverifiedEmail:\n      \"Vérification de l'adresse e-mail requise. Veuillez vérifier l'adresse e-mail utilisée avec votre fournisseur.\",\n    signupDisabled: \"L'enregistrement du compte est désactivé.\",\n    handlerNotFoundError:\n      \"L'étape actuelle n'est pas prise en charge dans cette version de l'application. Veuillez réessayer plus tard ou contacter l'équipe d'assistance pour obtenir de l'aide.\",\n  },\n  flowErrors: {\n    technical_error:\n      \"Une erreur technique s'est produite. Veuillez réessayer ultérieurement.\",\n    flow_expired_error:\n      \"La session a expiré, veuillez cliquer sur le bouton pour redémarrer.\",\n    value_invalid_error: \"La valeur saisie est invalide.\",\n    passcode_invalid: \"Le code fourni n'était pas correct.\",\n    passkey_invalid: \"Cette clé de passe ne peut plus être utilisée.\",\n    passcode_max_attempts_reached:\n      \"Le code a été entré incorrectement trop de fois. Veuillez demander un nouveau code.\",\n    rate_limit_exceeded:\n      \"Trop de demandes ont été effectuées. Veuillez patienter pour répéter l'opération demandée.\",\n    unknown_username_error: \"Le nom d'utilisateur est inconnu.\",\n    unknown_email_error: \"L'adresse e-mail est inconnue.\",\n    username_already_exists: \"Le nom d'utilisateur est déjà pris.\",\n    invalid_username_error:\n      \"Le nom d'utilisateur ne doit contenir que des lettres, des chiffres et des traits de soulignement.\",\n    email_already_exists: \"L'adresse e-mail est déjà utilisée.\",\n    not_found: \"La ressource demandée n'a pas été trouvée.\",\n    operation_not_permitted_error: \"L'opération n'est pas autorisée.\",\n    flow_discontinuity_error:\n      \"Le processus ne peut pas continuer en raison des paramètres utilisateur ou de la configuration du fournisseur.\",\n    form_data_invalid_error:\n      \"Les données du formulaire soumises contiennent des erreurs.\",\n    unauthorized: \"Votre session a expiré. Veuillez vous connecter à nouveau.\",\n    value_missing_error: \"La valeur est manquante.\",\n    value_too_long_error: \"La valeur est trop longue.\",\n    value_too_short_error: \"La valeur est trop courte.\",\n    webauthn_credential_invalid_mfa_only:\n      \"Cette identité peut être utilisée uniquement comme clé de sécurité de deuxième facteur.\",\n    webauthn_credential_already_exists:\n      \"La demande a expiré, a été annulée ou le dispositif est déjà enregistré. Veuillez réessayer ou essayer d'utiliser un autre dispositif.\",\n    platform_authenticator_required:\n      \"Votre compte est configuré pour utiliser des authentificateurs de plate-forme, mais votre appareil ou navigateur actuel ne prend pas en charge cette fonctionnalité. Veuillez réessayer avec un appareil ou un navigateur compatible.\",\n    third_party_access_denied:\n      \"Accès refusé. La demande a été annulée par l'utilisateur ou le fournisseur a refusé l'accès pour d'autres raisons.\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/it.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const it: Translation = {\n  headlines: {\n    error: \"Si è verificato un errore\",\n    loginEmail: \"Accedi o registrati\",\n    loginEmailNoSignup: \"Accedi\",\n    loginFinished: \"Accesso riuscito\",\n    loginPasscode: \"Inserisci codice di accesso\",\n    loginPassword: \"Inserisci password\",\n    registerAuthenticator: \"Crea una passkey\",\n    registerConfirm: \"Vuoi creare un account?\",\n    registerPassword: \"Imposta una nuova password\",\n    otpSetUp: \"Imposta l'app di autenticazione\",\n    profileEmails: \"Emails\",\n    profilePassword: \"Password\",\n    profilePasskeys: \"Passkeys\",\n    isPrimaryEmail: \"Indirizzo email principale\",\n    setPrimaryEmail: \"Imposta indirizzo email principale\",\n    createEmail: \"Inserisci una nuova email\",\n    createUsername: \"Inserisci un nuovo nome utente\",\n    emailVerified: \"Verificata\",\n    emailUnverified: \"Non verificata\",\n    emailDelete: \"Rimuovi\",\n    renamePasskey: \"Rinomina passkey\",\n    deletePasskey: \"Elimina passkey\",\n    lastUsedAt: \"Ultimo accesso il\",\n    createdAt: \"Creato il\",\n    connectedAccounts: \"Account collegati\",\n    deleteAccount: \"Cancella account\",\n    accountNotFound: \"Account non trovato\",\n    signIn: \"Accedi\",\n    signUp: \"Registrati\",\n    selectLoginMethod: \"Seleziona il metodo di accesso\",\n    setupLoginMethod: \"Imposta il metodo di accesso\",\n    lastUsed: \"Ultima visualizzazione\",\n    ipAddress: \"Indirizzo IP\",\n    revokeSession: \"Revoca sessione\",\n    profileSessions: \"Sessioni\",\n    mfaSetUp: \"Imposta MFA\",\n    securityKeySetUp: \"Aggiungi una chiave di sicurezza\",\n    securityKeyLogin: \"Chiave di sicurezza\",\n    otpLogin: \"Codice di autenticazione\",\n    renameSecurityKey: \"Rinomina la chiave di sicurezza\",\n    deleteSecurityKey: \"Elimina la chiave di sicurezza\",\n    securityKeys: \"Chiavi di sicurezza\",\n    authenticatorApp: \"App di autenticazione\",\n    authenticatorAppAlreadySetUp: \"L'app di autenticazione è già configurata\",\n    authenticatorAppNotSetUp: \"Imposta l'app di autenticazione\",\n    trustDevice: \"Fidarsi di questo browser?\",\n    deleteIdentity: \"Elimina connessione\",\n  },\n  texts: {\n    enterPasscode: \"Inserisci il codice di accesso inviato al tuo indirizzo email.\",\n    enterPasscodeNoEmail:\n      \"Inserisci il codice inviato al tuo indirizzo email principale.\",\n    setupPasskey:\n      \"Accedi al tuo account in modo semplice e sicuro con una passkey. Nota: I tuoi dati biometrici sono archiviati solo sui tuoi dispositivi e non saranno condivisi con nessuno.\",\n    createAccount:\n      'Nessun account trovato per \"{emailAddress}\". Vuoi creare un nuovo account?',\n    otpEnterVerificationCode:\n      \"Inserisci il codice di verifica generato dalla tua app di autenticazione:\",\n    otpScanQRCode:\n      \"Scansiona il codice QR con la tua app di autenticazione (come Google Authenticator o un'altra app TOTP). In alternativa, puoi inserire manualmente la chiave segreta OTP nell'app.\",\n    otpSecretKey: \"Chiave segreta OTP\",\n    passwordFormatHint:\n      \"La lunghezza della password deve essere compresa tra i {minLength} e {maxLength} caratteri.\",\n    setPrimaryEmail:\n      \"Imposta questo indirizzo email per essere utilizzato per contattarti.\",\n    isPrimaryEmail:\n      \"Questo indirizzo email verrà utilizzato per contattarti se necessario.\",\n    emailVerified: \"Questo indirizzo email è stato verificato.\",\n    emailUnverified: \"Questo indirizzo email non è stato verificato.\",\n    emailDelete:\n      \"Se cancelli questo indirizzo email, non potrà più essere utilizzato per accedere.\",\n    renamePasskey: \"Imposta un nome per la passkey.\",\n    deletePasskey: \"Cancella questa passkey dal tuo account.\",\n    deleteAccount:\n      \"Sei sicuro di voler cancellare questo account? Tutti i dati verranno immediatamente cancellati permanentemente e non potranno essere ripristinati.\",\n    noAccountExists: 'Non esiste alcun account per \"{emailAddress}\".',\n    selectLoginMethodForFutureLogins:\n      \"Seleziona uno dei seguenti metodi di accesso da utilizzare per i futuri accessi.\",\n    howDoYouWantToLogin: \"Come desideri effettuare l'accesso?\",\n    mfaSetUp:\n      \"Proteggi il tuo account con l'autenticazione a più fattori (MFA). La MFA aggiunge un ulteriore livello di sicurezza al tuo processo di accesso e garantisce che il tuo account rimanga protetto anche se la tua password o il tuo indirizzo email vengono compromessi.\",\n    securityKeyLogin:\n      \"Collega la tua chiave di sicurezza o attivala, quindi fai clic sul pulsante qui sotto. Una volta pronto, usala tramite USB, NFC o il tuo telefono. Segui le istruzioni per completare il processo di accesso.\",\n    otpLogin:\n      \"Apri la tua app di autenticazione per ottenere il codice OTP. Inserisci il codice nel campo qui sotto per completare il tuo accesso.\",\n    renameSecurityKey: \"Imposta un nome per la chiave di sicurezza.\",\n    deleteSecurityKey: \"Elimina questa chiave di sicurezza dal tuo account.\",\n    authenticatorAppAlreadySetUp:\n      \"Il tuo account è protetto da un'app di autenticazione che genera codici monouso (TOTP) per l'autenticazione a più fattori.\",\n    authenticatorAppNotSetUp:\n      \"Proteggi il tuo account con un'app di autenticazione che genera codici monouso (TOTP) per l'autenticazione a più fattori.\",\n    securityKeySetUp:\n      \"Utilizza una chiave di sicurezza dedicata tramite USB, Bluetooth o NFC oppure il tuo telefono. Collega la tua chiave di sicurezza o attivala, quindi fai clic sul pulsante qui sotto e segui le istruzioni per completare la registrazione.\",\n    trustDevice:\n      \"Se ti fidi di questo browser, non dovrai inserire il tuo OTP (One-Time Password) o utilizzare la tua chiave di sicurezza per l'autenticazione a più fattori (MFA) la prossima volta che accedi.\",\n  },\n  labels: {\n    or: \"o\",\n    no: \"no\",\n    yes: \"si\",\n    email: \"Email\",\n    continue: \"Continua\",\n    copied: \"copiato\",\n    skip: \"Salta\",\n    save: \"Salva\",\n    password: \"Password\",\n    passkey: \"Chiave di accesso\",\n    passcode: \"Codice di accesso\",\n    signInPassword: \"Accedi con password\",\n    signInPasscode: \"Accedi con codice di accesso\",\n    forgotYourPassword: \"Password dimenticata?\",\n    back: \"Indietro\",\n    signInPasskey: \"Accedi con passkey\",\n    registerAuthenticator: \"Crea una passkey\",\n    signIn: \"Accedi\",\n    signUp: \"Registrati\",\n    sendNewPasscode: \"Invia nuovo codice\",\n    passwordRetryAfter: \"Riprova tra {passwordRetryAfter}\",\n    passcodeResendAfter: \"Richiedi un nuovo codice tra {passcodeResendAfter}\",\n    unverifiedEmail: \"non verificato\",\n    primaryEmail: \"principale\",\n    setAsPrimaryEmail: \"Imposta come principale\",\n    verify: \"Verifica\",\n    delete: \"Cancella\",\n    newEmailAddress: \"Nuovo indirizzo email\",\n    newPassword: \"Nuova password\",\n    rename: \"Rinomina\",\n    newPasskeyName: \"Nuovo nome passkey\",\n    addEmail: \"Aggiungi email\",\n    createPasskey: \"Crea una passkey\",\n    webauthnUnsupported: \"Le Passkeys non sono supportate dal tuo browser\",\n    signInWith: \"Accedi con {provider}\",\n    deleteAccount: \"Sì, cancella questo account.\",\n    emailOrUsername: \"E-mail o Nome utente\",\n    username: \"Nome utente\",\n    optional: \"opzionale\",\n    dontHaveAnAccount: \"Non hai un account?\",\n    alreadyHaveAnAccount: \"Hai già un account?\",\n    changeUsername: \"Cambia nome utente\",\n    setUsername: \"Imposta nome utente\",\n    changePassword: \"Cambia password\",\n    setPassword: \"Imposta password\",\n    revoke: \"Revoca\",\n    currentSession: \"Sessione corrente\",\n    authenticatorApp: \"App di autenticazione\",\n    securityKey: \"Chiave di sicurezza\",\n    securityKeyUse: \"Usa chiave di sicurezza\",\n    newSecurityKeyName: \"Nuovo nome chiave di sicurezza\",\n    createSecurityKey: \"Aggiungi una chiave di sicurezza\",\n    authenticatorAppManage: \"Gestisci app di autenticazione\",\n    authenticatorAppAdd: \"Imposta\",\n    configured: \"configurato\",\n    useAnotherMethod: \"Usa un altro metodo\",\n    lastUsed: \"Ultimo utilizzo\",\n    trustDevice: \"Fidarsi di questo browser\",\n    staySignedIn: \"Rimani connesso\",\n    connectAccount: \"Collega account\",\n  },\n  errors: {\n    somethingWentWrong: \"Si è verificato un errore tecnico. Riprova più tardi.\",\n    requestTimeout: \"La richiesta è scaduta.\",\n    invalidPassword: \"Email o password sbagliata.\",\n    invalidPasscode: \"Il codice di accesso inserito non è corretto.\",\n    passcodeAttemptsReached:\n      \"Il codice di accesso è stato inserito in modo errato troppe volte. Richiedi un nuovo codice.\",\n    tooManyRequests:\n      \"Sono state effettuate troppe richieste. Attenti per ripetere l'operazione richiesta.\",\n    unauthorized: \"La sessione è scaduta. Riprova ad accedere.\",\n    invalidWebauthnCredential: \"La passkey non può più essere utilizzata.\",\n    passcodeExpired:\n      \"Il codice di accesso è scaduto. Richiedi un nuovo codice.\",\n    userVerification:\n      \"Verifica utente richiesta. Assicurati che il tuo dispositivo di autenticazione è protetto con un PIN o un codice biometrico.\",\n    emailAddressAlreadyExistsError: \"L'indirizzo email è già stato utilizzato.\",\n    maxNumOfEmailAddressesReached:\n      \"Non è possibile aggiungere ulteriori indirizzi email.\",\n    thirdPartyAccessDenied:\n      \"Accesso negato. La richiesta è stata cancellata dall'utente o il provider ha negato l'accesso per altre ragioni.\",\n    thirdPartyMultipleAccounts:\n      \"Impossibile identificare l'account. L'indirizzo email è utilizzato in più account.\",\n    thirdPartyUnverifiedEmail:\n      \"Verifica email richiesta. Verifica l'indirizzo email utilizzato con il tuo provider.\",\n    signupDisabled: \"La registrazione dell'account è disabilitata.\",\n    handlerNotFoundError:\n      \"Il passaggio corrente non è supportato in questa versione dell'applicazione. Per favore riprova più tardi o contatta il team di supporto per ricevere assistenza.\",\n  },\n  flowErrors: {\n    technical_error: \"Si è verificato un errore tecnico. Riprova più tardi.\",\n    flow_expired_error:\n      \"La sessione è scaduta, clicca sul pulsante per riavviare.\",\n    value_invalid_error: \"Il valore inserito non è valido.\",\n    passcode_invalid: \"Il codice inserito non è corretto.\",\n    passkey_invalid: \"Questa chiave di accesso non può più essere utilizzata.\",\n    passcode_max_attempts_reached:\n      \"Il codice è stato inserito troppe volte in modo errato. Si prega di richiedere un nuovo codice.\",\n    rate_limit_exceeded:\n      \"Troppe richieste sono state effettuate. Si prega di attendere per ripetere l'operazione richiesta.\",\n    unknown_username_error: \"Il nome utente è sconosciuto.\",\n    unknown_email_error: \"L'indirizzo email è sconosciuto.\",\n    username_already_exists: \"Il nome utente è già in uso.\",\n    invalid_username_error:\n      \"Il nome utente deve contenere solo lettere, numeri e trattini bassi.\",\n    email_already_exists: \"L'indirizzo email è già in uso.\",\n    not_found: \"La risorsa richiesta non è stata trovata.\",\n    operation_not_permitted_error: \"L'operazione non è consentita.\",\n    flow_discontinuity_error:\n      \"Il processo non può essere continuato a causa delle impostazioni dell'utente o della configurazione del fornitore.\",\n    form_data_invalid_error: \"I dati del modulo inviato contengono errori.\",\n    unauthorized: \"La sessione è scaduta. Riprova ad accedere.\",\n    value_missing_error: \"Il valore è mancante.\",\n    value_too_long_error: \"Il valore è troppo lungo.\",\n    value_too_short_error: \"Il valore è troppo corto.\",\n    webauthn_credential_invalid_mfa_only:\n      \"Questa identità può essere utilizzata solo come secondo fattore di autenticazione.\",\n    webauthn_credential_already_exists:\n      \"La richiesta è scaduta, è stata annullata o il dispositivo è già registrato. Prova di nuovo o usa un altro dispositivo.\",\n    platform_authenticator_required:\n      \"Il tuo account è configurato per utilizzare gli autenticatori di piattaforma. Tuttavia, il tuo dispositivo o browser corrente non supporta questa funzione. Prova di nuovo con un dispositivo o un browser compatibile.\",\n    third_party_access_denied:\n      \"Accesso negato. La richiesta è stata cancellata dall'utente o il provider ha negato l'accesso per altre ragioni.\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/nl.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const nl: Translation = {\n  headlines: {\n    error: \"Er is een fout opgetreden\",\n    loginEmail: \"Inloggen of account aanmaken\",\n    loginEmailNoSignup: \"Inloggen\",\n    loginFinished: \"Inloggen geslaagd\",\n    loginPasscode: \"Voer toegangscode in\",\n    loginPassword: \"Voer wachtwoord in\",\n    registerAuthenticator: \"Maak een passkey aan\",\n    registerConfirm: \"Account aanmaken?\",\n    registerPassword: \"Stel nieuw wachtwoord in\",\n    otpSetUp: \"Stel authenticator-app in\",\n    profileEmails: \"E-mailadressen\",\n    profilePassword: \"Wachtwoord\",\n    profilePasskeys: \"Passkeys\",\n    isPrimaryEmail: \"Primair e-mailadres\",\n    setPrimaryEmail: \"Instellen als primair e-mailadres\",\n    createEmail: \"Voer een nieuw e-mailadres in\",\n    createUsername: \"Voer een nieuwe gebruikersnaam in\",\n    emailVerified: \"Geverifieerd\",\n    emailUnverified: \"Niet geverifieerd\",\n    emailDelete: \"Verwijderen\",\n    renamePasskey: \"Passkey hernoemen\",\n    deletePasskey: \"Passkey verwijderen\",\n    lastUsedAt: \"Laatst gebruikt op\",\n    createdAt: \"Aangemaakt op\",\n    connectedAccounts: \"Gekoppelde accounts\",\n    deleteAccount: \"Account verwijderen\",\n    accountNotFound: \"Account niet gevonden\",\n    signIn: \"Inloggen\",\n    signUp: \"Account aanmaken\",\n    selectLoginMethod: \"Selecteer inlogmethode\",\n    setupLoginMethod: \"Stel inlogmethode in\",\n    lastUsed: \"Laatst gezien\",\n    ipAddress: \"IP-adres\",\n    revokeSession: \"Sessie intrekken\",\n    profileSessions: \"Sessies\",\n    mfaSetUp: \"MFA instellen\",\n    securityKeySetUp: \"Beveiligingssleutel toevoegen\",\n    securityKeyLogin: \"Beveiligingssleutel\",\n    otpLogin: \"Authenticatiecode\",\n    renameSecurityKey: \"Beveiligingssleutel hernoemen\",\n    deleteSecurityKey: \"Beveiligingssleutel verwijderen\",\n    securityKeys: \"Beveiligingssleutels\",\n    authenticatorApp: \"Authenticator-app\",\n    authenticatorAppAlreadySetUp: \"Authenticator-app is ingesteld\",\n    authenticatorAppNotSetUp: \"Stel authenticator-app in\",\n    trustDevice: \"Deze browser vertrouwen?\",\n    deleteIdentity: \"Verbinding verwijderen\",\n  },\n  texts: {\n    enterPasscode: \"Voer de toegangscode in die naar uw e-mailadres is verzonden.\",\n    enterPasscodeNoEmail:\n      \"Voer de toegangscode in die naar uw primaire e-mailadres is verzonden.\",\n    setupPasskey:\n      \"Log eenvoudig en veilig in op uw account met een passkey. Opmerking: Uw biometrische gegevens worden alleen op uw apparaten opgeslagen en nooit met iemand gedeeld.\",\n    createAccount:\n      'Er bestaat geen account voor \"{emailAddress}\". Wilt u een nieuw account aanmaken?',\n    otpEnterVerificationCode:\n      \"Voer hieronder de eenmalige wachtwoordcode (OTP) in die u van uw authenticator-app heeft gekregen:\",\n    otpScanQRCode:\n      \"Scan de QR-code met uw authenticator-app (zoals Google Authenticator of een andere TOTP-app). U kunt ook handmatig de geheime OTP-sleutel in de app invoeren.\",\n    otpSecretKey: \"Geheime OTP-sleutel\",\n    passwordFormatHint:\n      \"Moet tussen {minLength} en {maxLength} tekens lang zijn.\",\n    securityKeySetUp:\n      \"Gebruik een speciale beveiligingssleutel via USB, Bluetooth of NFC, of uw mobiele telefoon. Sluit uw beveiligingssleutel aan of activeer deze, klik vervolgens op de knop hieronder en volg de instructies om de registratie te voltooien.\",\n    setPrimaryEmail: \"Stel dit e-mailadres in om gebruikt te worden om contact met u op te nemen.\",\n    isPrimaryEmail:\n      \"Dit e-mailadres wordt gebruikt om contact met u op te nemen indien nodig.\",\n    emailVerified: \"Dit e-mailadres is geverifieerd.\",\n    emailUnverified: \"Dit e-mailadres is niet geverifieerd.\",\n    emailDelete:\n      \"Als u dit e-mailadres verwijdert, kan het niet langer worden gebruikt om in te loggen.\",\n    renamePasskey: \"Geef de passkey een naam.\",\n    deletePasskey: \"Verwijder deze passkey van uw account.\",\n    deleteAccount:\n      \"Weet u zeker dat u dit account wilt verwijderen? Alle gegevens worden onmiddellijk verwijderd en kunnen niet worden hersteld.\",\n    noAccountExists: 'Er bestaat geen account voor \"{emailAddress}\".',\n    selectLoginMethodForFutureLogins:\n      \"Selecteer een van de volgende inlogmethoden om te gebruiken voor toekomstige inlogpogingen.\",\n    howDoYouWantToLogin: \"Hoe wilt u inloggen?\",\n    mfaSetUp:\n      \"Bescherm uw account met Multi-Factor Authenticatie (MFA). MFA voegt een extra stap toe aan uw inlogproces, waardoor uw account veilig blijft, zelfs als uw wachtwoord of e-mailaccount gecompromitteerd is.\",\n    securityKeyLogin:\n      \"Sluit uw beveiligingssleutel aan of activeer deze en klik vervolgens op de knop hieronder. Zodra u klaar bent, gebruikt u deze via USB, NFC of uw mobiele telefoon. Volg de instructies om het inlogproces te voltooien.\",\n    otpLogin:\n      \"Open uw authenticator-app om het eenmalige wachtwoord (OTP) te verkrijgen. Voer de code in het onderstaande veld in om uw login te voltooien.\",\n    renameSecurityKey: \"Geef de beveiligingssleutel een naam.\",\n    deleteSecurityKey: \"Verwijder deze beveiligingssleutel van uw account.\",\n    authenticatorAppAlreadySetUp:\n      \"Uw account is beveiligd met een authenticator-app die tijdsgebonden eenmalige wachtwoorden (TOTP) genereert voor multi-factor authenticatie.\",\n    authenticatorAppNotSetUp:\n      \"Beveilig uw account met een authenticator-app die tijdsgebonden eenmalige wachtwoorden (TOTP) genereert voor multi-factor authenticatie.\",\n    trustDevice:\n      \"Als u deze browser vertrouwt, hoeft u de volgende keer dat u inlogt geen OTP (One-Time-Password) in te voeren of uw beveiligingssleutel te gebruiken voor multi-factor authenticatie (MFA).\",\n  },\n  labels: {\n    or: \"of\",\n    no: \"nee\",\n    yes: \"ja\",\n    email: \"E-mail\",\n    continue: \"Doorgaan\",\n    copied: \"gekopieerd\",\n    skip: \"Overslaan\",\n    save: \"Opslaan\",\n    password: \"Wachtwoord\",\n    passkey: \"Passkey\",\n    passcode: \"Toegangscode\",\n    signInPassword: \"Inloggen met een wachtwoord\",\n    signInPasscode: \"Inloggen met een toegangscode\",\n    forgotYourPassword: \"Wachtwoord vergeten?\",\n    back: \"Terug\",\n    signInPasskey: \"Inloggen met een passkey\",\n    registerAuthenticator: \"Maak een passkey aan\",\n    signIn: \"Inloggen\",\n    signUp: \"Account aanmaken\",\n    sendNewPasscode: \"Stuur nieuwe code\",\n    passwordRetryAfter: \"Probeer opnieuw over {passwordRetryAfter}\",\n    passcodeResendAfter: \"Vraag een nieuwe code aan over {passcodeResendAfter}\",\n    unverifiedEmail: \"niet geverifieerd\",\n    primaryEmail: \"primair\",\n    setAsPrimaryEmail: \"Instellen als primair\",\n    verify: \"Verifiëren\",\n    delete: \"Verwijderen\",\n    newEmailAddress: \"Nieuw e-mailadres\",\n    newPassword: \"Nieuw wachtwoord\",\n    rename: \"Hernoemen\",\n    newPasskeyName: \"Nieuwe passkey-naam\",\n    addEmail: \"E-mail toevoegen\",\n    createPasskey: \"Passkey aanmaken\",\n    webauthnUnsupported: \"Passkeys worden niet ondersteund door uw browser\",\n    signInWith: \"Doorgaan met {provider}\",\n    deleteAccount: \"Ja, verwijder dit account.\",\n    emailOrUsername: \"E-mail of gebruikersnaam\",\n    username: \"Gebruikersnaam\",\n    optional: \"optioneel\",\n    dontHaveAnAccount: \"Heeft u geen account?\",\n    alreadyHaveAnAccount: \"Heeft u al een account?\",\n    changeUsername: \"Gebruikersnaam wijzigen\",\n    setUsername: \"Gebruikersnaam instellen\",\n    changePassword: \"Wachtwoord wijzigen\",\n    setPassword: \"Wachtwoord instellen\",\n    revoke: \"Intrekken\",\n    currentSession: \"Huidige sessie\",\n    authenticatorApp: \"Authenticator-app\",\n    securityKey: \"Beveiligingssleutel\",\n    securityKeyUse: \"Gebruik beveiligingssleutel\",\n    newSecurityKeyName: \"Nieuwe naam beveiligingssleutel\",\n    createSecurityKey: \"Beveiligingssleutel toevoegen\",\n    authenticatorAppManage: \"Beheer authenticator-app\",\n    authenticatorAppAdd: \"Instellen\",\n    configured: \"geconfigureerd\",\n    useAnotherMethod: \"Gebruik een andere methode\",\n    lastUsed: \"Laatst gebruikt\",\n    trustDevice: \"Vertrouw deze browser\",\n    staySignedIn: \"Ingelogd blijven\",\n    connectAccount: \"Account verbinden\",\n  },\n  errors: {\n    somethingWentWrong:\n      \"Er is een technische fout opgetreden. Probeer het later opnieuw.\",\n    requestTimeout: \"Het verzoek is verlopen.\",\n    invalidPassword: \"Verkeerd e-mailadres of wachtwoord.\",\n    invalidPasscode: \"De ingevoerde toegangscode was niet correct.\",\n    passcodeAttemptsReached:\n      \"De toegangscode is te vaak onjuist ingevoerd. Vraag een nieuwe code aan.\",\n    tooManyRequests:\n      \"Er zijn te veel verzoeken gedaan. Wacht even voordat u de gevraagde handeling herhaalt.\",\n    unauthorized: \"Uw sessie is verlopen. Log opnieuw in.\",\n    invalidWebauthnCredential: \"Deze passkey kan niet meer worden gebruikt.\",\n    passcodeExpired: \"De toegangscode is verlopen. Vraag een nieuwe aan.\",\n    userVerification:\n      \"Gebruikersverificatie vereist. Zorg ervoor dat uw authenticator-apparaat beveiligd is met een pincode of biometrie.\",\n    emailAddressAlreadyExistsError: \"Het e-mailadres bestaat al.\",\n    maxNumOfEmailAddressesReached: \"Er kunnen geen e-mailadressen meer worden toegevoegd.\",\n    thirdPartyAccessDenied:\n      \"Toegang geweigerd. Het verzoek is geannuleerd door de gebruiker of de provider heeft de toegang om andere redenen geweigerd.\",\n    thirdPartyMultipleAccounts:\n      \"Kan account niet identificeren. Het e-mailadres wordt door meerdere accounts gebruikt.\",\n    thirdPartyUnverifiedEmail:\n      \"E-mailverificatie vereist. Verifieer het gebruikte e-mailadres bij uw provider.\",\n    signupDisabled: \"Accountregistratie is uitgeschakeld.\",\n    handlerNotFoundError:\n      \"De huidige stap in uw proces wordt niet ondersteund door deze applicatieversie. Probeer het later opnieuw of neem contact op met support als het probleem aanhoudt.\",\n  },\n  flowErrors: {\n    technical_error: \"Er is een technische fout opgetreden. Probeer het later opnieuw.\",\n    flow_expired_error:\n      \"De sessie is verlopen, klik op de knop om opnieuw te starten.\",\n    value_invalid_error: \"De ingevoerde waarde is ongeldig.\",\n    passcode_invalid: \"De ingevoerde toegangscode was niet correct.\",\n    passkey_invalid: \"Deze passkey kan niet meer worden gebruikt.\",\n    passcode_max_attempts_reached:\n      \"De toegangscode is te vaak onjuist ingevoerd. Vraag een nieuwe code aan.\",\n    rate_limit_exceeded:\n      \"Er zijn te veel verzoeken gedaan. Wacht even voordat u de gevraagde handeling herhaalt.\",\n    unknown_username_error: \"De gebruikersnaam is onbekend.\",\n    unknown_email_error: \"Het e-mailadres is onbekend.\",\n    username_already_exists: \"De gebruikersnaam is al in gebruik.\",\n    invalid_username_error:\n      \"De gebruikersnaam mag alleen letters, cijfers en underscores bevatten.\",\n    email_already_exists: \"Het e-mailadres is al in gebruik.\",\n    not_found: \"De gevraagde bron is niet gevonden.\",\n    operation_not_permitted_error: \"De handeling is niet toegestaan.\",\n    flow_discontinuity_error:\n      \"Het proces kan niet worden voortgezet vanwege gebruikersinstellingen of de configuratie van de provider.\",\n    form_data_invalid_error: \"De ingediende formuliergegevens bevatten fouten.\",\n    unauthorized: \"Uw sessie is verlopen. Log opnieuw in.\",\n    value_missing_error: \"De waarde ontbreekt.\",\n    value_too_long_error: \"Waarde is te lang.\",\n    value_too_short_error: \"De waarde is te kort.\",\n    webauthn_credential_invalid_mfa_only:\n      \"Deze referentie kan alleen worden gebruikt als een tweede factor beveiligingssleutel.\",\n    webauthn_credential_already_exists:\n      \"Het verzoek is verlopen, geannuleerd, of het apparaat is al geregistreerd. Probeer het opnieuw of gebruik een ander apparaat.\",\n    platform_authenticator_required:\n      \"Uw account is geconfigureerd om platform-authenticators te gebruiken, maar uw huidige apparaat of browser ondersteunt deze functie niet. Probeer het opnieuw met een compatibel apparaat of browser.\",\n    third_party_access_denied:\n      \"Toegang geweigerd. Het verzoek is geannuleerd door de gebruiker of de provider heeft de toegang om andere redenen geweigerd.\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/pt-BR.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const ptBR: Translation = {\n  headlines: {\n    error: \"Ocorreu um erro\",\n    loginEmail: \"Entre ou cadastre-se\",\n    loginEmailNoSignup: \"Entre\",\n    loginFinished: \"Login efetuado com sucesso\",\n    loginPasscode: \"Digite o código de acesso\",\n    loginPassword: \"Digite a senha\",\n    registerAuthenticator: \"Criar uma chave de acesso\",\n    registerConfirm: \"Concluir cadastro?\",\n    registerPassword: \"Redefina sua senha\",\n    otpSetUp: \"Configurar o aplicativo de autenticação\",\n    profileEmails: \"E-mails\",\n    profilePassword: \"Senha\",\n    profilePasskeys: \"Chave de acesso\",\n    isPrimaryEmail: \"E-mail principal\",\n    setPrimaryEmail: \"Definir o e-mail principal\",\n    createEmail: \"Digite um novo e-mail\",\n    createUsername: \"Digite um novo nome de usuário\",\n    emailVerified: \"Verificado\",\n    emailUnverified: \"Não verificado\",\n    emailDelete: \"Apagar\",\n    renamePasskey: \"Renomear a chave de acesso\",\n    deletePasskey: \"Apagar a chave de acesso\",\n    lastUsedAt: \"Usado pela última vez em\",\n    createdAt: \"Criado em\",\n    connectedAccounts: \"Contas conectadas\",\n    deleteAccount: \"Apagar a conta\",\n    accountNotFound: \"Conta não encontrada\",\n    signIn: \"Entrar\",\n    signUp: \"Registrar\",\n    selectLoginMethod: \"Selecionar método de login\",\n    setupLoginMethod: \"Configurar método de login\",\n    lastUsed: \"Última vez visto\",\n    ipAddress: \"Endereço IP\",\n    revokeSession: \"Revogar sessão\",\n    profileSessions: \"Sessões\",\n    mfaSetUp: \"Configurar MFA\",\n    securityKeySetUp: \"Adicionar uma chave de segurança\",\n    securityKeyLogin: \"Chave de segurança\",\n    otpLogin: \"Código de autenticação\",\n    renameSecurityKey: \"Renomear chave de segurança\",\n    deleteSecurityKey: \"Excluir chave de segurança\",\n    securityKeys: \"Chaves de segurança\",\n    authenticatorApp: \"Aplicativo de autenticação\",\n    authenticatorAppAlreadySetUp:\n      \"O aplicativo de autenticação já está configurado\",\n    authenticatorAppNotSetUp: \"Configurar o aplicativo de autenticação\",\n    trustDevice: \"Confiar neste navegador?\",\n    deleteIdentity: \"Excluir conexão\",\n  },\n  texts: {\n    enterPasscode:\n      \"Digite o código de acesso enviado para o seu endereço de email.\",\n    enterPasscodeNoEmail:\n      \"Digite o código enviado para o seu endereço de e-mail principal.\",\n    setupPasskey:\n      \"Entre na sua conta de forma fácil e segura com uma chave de acesso. Nota: Os seus dados biométricos são apenas guardados no seu aparelho e nunca serão compartilhados com ninguém.\",\n    createAccount:\n      'Nenhuma conta encontrada para o e-mail \"{emailAddress}\". Deseja criar uma nova conta?',\n    otpEnterVerificationCode:\n      \"Insira o código de verificação gerado pelo seu aplicativo de autenticação:\",\n    otpScanQRCode:\n      \"Escaneie o código QR com seu aplicativo de autenticação (como Google Authenticator ou outro aplicativo TOTP). Alternativamente, você pode inserir manualmente a chave secreta OTP no aplicativo.\",\n    otpSecretKey: \"Chave secreta OTP\",\n    passwordFormatHint:\n      \"Deve conter entre {minLength} e {maxLength} caracteres.\",\n    setPrimaryEmail:\n      \"Defina este endereço de e-mail para ser usado para entrar em contato com você.\",\n    isPrimaryEmail:\n      \"Este endereço de e-mail será usado para entrar em contato com você, se necessário.\",\n    emailVerified: \"Este e-mail foi verificado.\",\n    emailUnverified: \"Este e-mail não foi verificado.\",\n    emailDelete:\n      \"Se você apagar esse e-mail, não poderá mais usá-lo para entrar em sua conta.\",\n    renamePasskey: \"Defina um nome para a chave de acesso.\",\n    deletePasskey: \"Remova essa chave de acesso da sua conta.\",\n    deleteAccount:\n      \"Tem certeza que deseja apagar esta conta? Todos os dados serão apagados imediatamente e não poderão ser recuperados.\",\n    noAccountExists: 'Nenhuma conta encontrada para o e-mail \"{emailAddress}\".',\n    selectLoginMethodForFutureLogins:\n      \"Selecione um dos métodos de login a seguir para usar em logins futuros.\",\n    howDoYouWantToLogin: \"Como você deseja fazer login?\",\n    mfaSetUp:\n      \"Proteja sua conta com autenticação de múltiplos fatores (MFA). A MFA adiciona uma camada extra de segurança ao seu processo de login e garante que sua conta permaneça protegida mesmo que sua senha ou endereço de e-mail sejam comprometidos.\",\n    securityKeyLogin:\n      \"Conecte sua chave de segurança ou ative-a, em seguida, clique no botão abaixo. Quando estiver pronto, use-a via USB, NFC ou seu telefone. Siga as instruções para concluir o processo de login.\",\n    otpLogin:\n      \"Abra seu aplicativo de autenticação para obter o código OTP. Insira o código no campo abaixo para concluir seu login.\",\n    renameSecurityKey: \"Defina um nome para a chave de segurança.\",\n    deleteSecurityKey: \"Exclua esta chave de segurança da sua conta.\",\n    authenticatorAppAlreadySetUp:\n      \"Sua conta está protegida por um aplicativo de autenticação que gera códigos únicos (TOTP) para autenticação de múltiplos fatores.\",\n    authenticatorAppNotSetUp:\n      \"Proteja sua conta com um aplicativo de autenticação que gera códigos únicos (TOTP) para autenticação de múltiplos fatores.\",\n    securityKeySetUp:\n      \"Use uma chave de segurança dedicada via USB, Bluetooth ou NFC ou seu telefone. Conecte sua chave de segurança ou ative-a, em seguida, clique no botão abaixo e siga as instruções para concluir o registro.\",\n    trustDevice:\n      \"Se você confiar neste navegador, não precisará digitar seu OTP (Senha Única) ou usar sua chave de segurança para autenticação multifator (MFA) na próxima vez que fizer login.\",\n  },\n  labels: {\n    or: \"ou\",\n    no: \"não\",\n    yes: \"sim\",\n    email: \"E-mail\",\n    continue: \"Continuar\",\n    copied: \"copiado\",\n    skip: \"Pular\",\n    save: \"Salvar\",\n    password: \"Senha\",\n    passkey: \"Chave de acesso\",\n    passcode: \"Código de acesso\",\n    signInPassword: \"Entre com uma senha\",\n    signInPasscode: \"Entre com um código de acesso\",\n    forgotYourPassword: \"Esqueceu a sua senha?\",\n    back: \"Voltar\",\n    signInPasskey: \"Entre com uma chave de acesso\",\n    registerAuthenticator: \"Criar uma chave de acesso\",\n    signIn: \"Entrar\",\n    signUp: \"Cadastrar-se\",\n    sendNewPasscode: \"Enviar novo código\",\n    passwordRetryAfter: \"Tente novamente em {passwordRetryAfter}\",\n    passcodeResendAfter: \"Peça outro código em {passcodeResendAfter}\",\n    unverifiedEmail: \"Não verificado\",\n    primaryEmail: \"principal\",\n    setAsPrimaryEmail: \"Definir como principal\",\n    verify: \"Verificar\",\n    delete: \"Apagar\",\n    newEmailAddress: \"Novo e-mail\",\n    newPassword: \"Nova senha\",\n    rename: \"Renomear\",\n    newPasskeyName: \"Novo nome para a chave de acesso\",\n    addEmail: \"Adicionar e-mail\",\n    createPasskey: \"Criar uma chave de acesso\",\n    webauthnUnsupported:\n      \"Chaves de acesso não são compatíveis com seu navegador\",\n    signInWith: \"Entre com {provider}\",\n    deleteAccount: \"Sim, apagar esta conta.\",\n    emailOrUsername: \"E-mail ou Nome de usuário\",\n    username: \"Nome de usuário\",\n    optional: \"opcional\",\n    dontHaveAnAccount: \"Não tem uma conta?\",\n    alreadyHaveAnAccount: \"Já tem uma conta?\",\n    changeUsername: \"Alterar nome de usuário\",\n    setUsername: \"Definir nome de usuário\",\n    changePassword: \"Alterar senha\",\n    setPassword: \"Definir senha\",\n    revoke: \"Revogar\",\n    currentSession: \"Sessão atual\",\n    authenticatorApp: \"Aplicativo de autenticação\",\n    securityKey: \"Chave de segurança\",\n    securityKeyUse: \"Usar chave de segurança\",\n    newSecurityKeyName: \"Novo nome da chave de segurança\",\n    createSecurityKey: \"Adicionar uma chave de segurança\",\n    authenticatorAppManage: \"Gerenciar aplicativo de autenticação\",\n    authenticatorAppAdd: \"Configurar\",\n    configured: \"configurado\",\n    useAnotherMethod: \"Usar outro método\",\n    lastUsed: \"Último uso\",\n    trustDevice: \"Confiar neste navegador\",\n    staySignedIn: \"Manter-me conectado\",\n    connectAccount: \"Conectar conta\",\n  },\n  errors: {\n    somethingWentWrong:\n      \"Ocorreu um erro técnico. Por favor, tente novamente mais tarde.\",\n    requestTimeout: \"A página demorou demais para se conectar.\",\n    invalidPassword: \"E-mail ou senha inválido.\",\n    invalidPasscode: \"O código de acesso inserido não é válido.\",\n    passcodeAttemptsReached:\n      \"Um código de acesso inválido foi inserido várias vezes. Por favor, solicite um novo código.\",\n    tooManyRequests:\n      \"Muitas tentativas foram feitas. Aguarde alguns minutos antes de tentar novamente.\",\n    unauthorized: \"A sua sessão expirou. Inicie uma nova sessão.\",\n    invalidWebauthnCredential: \"Esta chave de acesso já não pode ser usada.\",\n    passcodeExpired:\n      \"O seu código de acesso expirou. Por favor, solicite um novo.\",\n    userVerification:\n      \"Verificação de usuário necessária. Por favor, verifique se o seu dispositivo de verificação está protegido com um PIN ou biometria.\",\n    emailAddressAlreadyExistsError: \"Este endereço de e-mail já existe.\",\n    maxNumOfEmailAddressesReached: \"Não é possível adicionar mais e-mails.\",\n    thirdPartyAccessDenied:\n      \"Acesso negado. O pedido foi cancelado pelo usuário ou o provedor negou o acesso por outros motivos.\",\n    thirdPartyMultipleAccounts:\n      \"Não foi possível identificar a conta. O endereço de e-mail é usado por várias contas.\",\n    thirdPartyUnverifiedEmail:\n      \"Verificação de e-mail necessária. Por favor, verifique o e-mail utilizado com o seu provedor.\",\n    signupDisabled: \"O registro da conta está desativado.\",\n    handlerNotFoundError:\n      \"O passo atual não é suportado nesta versão do aplicativo. Por favor, tente novamente mais tarde ou entre em contato com a equipe de suporte para obter ajuda.\",\n  },\n  flowErrors: {\n    technical_error:\n      \"Ocorreu um erro técnico. Por favor, tente novamente mais tarde.\",\n    flow_expired_error:\n      \"A sessão expirou, por favor, clique no botão para reiniciar.\",\n    value_invalid_error: \"O valor inserido é inválido.\",\n    passcode_invalid: \"O código fornecido não estava correto.\",\n    passkey_invalid: \"Esta chave de acesso não pode mais ser utilizada.\",\n    passcode_max_attempts_reached:\n      \"O código foi inserido incorretamente várias vezes. Por favor, solicite um novo código.\",\n    rate_limit_exceeded:\n      \"Foram feitas muitas solicitações. Por favor, aguarde para repetir a operação solicitada.\",\n    unknown_username_error: \"O nome de usuário é desconhecido.\",\n    unknown_email_error: \"O endereço de e-mail é desconhecido.\",\n    username_already_exists: \"O nome de usuário já está em uso.\",\n    invalid_username_error:\n      \"O nome de usuário deve conter apenas letras, números e sublinhados.\",\n    email_already_exists: \"O e-mail já está em uso.\",\n    not_found: \"O recurso solicitado não foi encontrado.\",\n    operation_not_permitted_error: \"A operação não é permitida.\",\n    flow_discontinuity_error:\n      \"O processo não pode ser continuado devido às configurações do usuário ou do provedor.\",\n    form_data_invalid_error: \"Os dados do formulário submetido contêm erros.\",\n    unauthorized: \"A sua sessão expirou. Inicie uma nova sessão.\",\n    value_missing_error: \"O valor está ausente.\",\n    value_too_long_error: \"O valor é muito longo.\",\n    value_too_short_error: \"O valor é muito curto.\",\n    webauthn_credential_invalid_mfa_only:\n      \"Esta identidade pode ser usada apenas como segundo fator de autenticação.\",\n    webauthn_credential_already_exists:\n      \"A solicitação expirou, foi cancelada ou o dispositivo já está registrado. Tente novamente ou use outro dispositivo.\",\n    platform_authenticator_required:\n      \"Sua conta está configurada para usar autentificadores de plataforma. No entanto, seu dispositivo ou navegador atual não suporta esse recurso. Tente novamente com um dispositivo ou navegador compatível.\",\n    third_party_access_denied:\n      \"Acesso negado. O pedido foi cancelado pelo usuário ou o provedor negou o acesso por outros motivos.\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/translations.ts",
    "content": "import { en } from \"./en\";\n\nexport interface Translations {\n  [lang: string]: Partial<Translation>;\n}\n\nexport interface Translation {\n  headlines: {\n    error: string;\n    accountNotFound: string;\n    authenticatorApp: string;\n    authenticatorAppAlreadySetUp: string;\n    authenticatorAppNotSetUp: string;\n    loginEmail: string;\n    loginEmailNoSignup: string;\n    loginFinished: string;\n    loginPasscode: string;\n    loginPassword: string;\n    registerAuthenticator: string;\n    registerConfirm: string;\n    registerPassword: string;\n    securityKeyLogin: string;\n    securityKeySetUp: string;\n    trustDevice: string;\n    mfaSetUp: string;\n    otpLogin: string;\n    otpSetUp: string;\n    profileEmails: string;\n    profilePassword: string;\n    profilePasskeys: string;\n    isPrimaryEmail: string;\n    setPrimaryEmail: string;\n    createEmail: string;\n    createUsername: string;\n    emailVerified: string;\n    emailUnverified: string;\n    emailDelete: string;\n    renamePasskey: string;\n    deletePasskey: string;\n    lastUsedAt: string;\n    createdAt: string;\n    connectedAccounts: string;\n    deleteAccount: string;\n    signIn: string;\n    signUp: string;\n    selectLoginMethod: string;\n    setupLoginMethod: string;\n    lastUsed: string;\n    ipAddress: string;\n    revokeSession: string;\n    profileSessions: string;\n    renameSecurityKey: string;\n    deleteSecurityKey: string;\n    securityKeys: string;\n    deleteIdentity: string;\n  };\n  texts: {\n    authenticatorAppAlreadySetUp: string;\n    authenticatorAppNotSetUp: string;\n    enterPasscode: string;\n    enterPasscodeNoEmail: string;\n    setupPasskey: string;\n    createAccount: string;\n    mfaSetUp: string;\n    noAccountExists: string;\n    otpEnterVerificationCode: string;\n    otpLogin: string;\n    otpScanQRCode: string;\n    otpSecretKey: string;\n    passwordFormatHint: string;\n    securityKeyLogin: string;\n    trustDevice: string;\n    isPrimaryEmail: string;\n    securityKeySetUp: string;\n    setPrimaryEmail: string;\n    emailVerified: string;\n    emailUnverified: string;\n    emailDelete: string;\n    renamePasskey: string;\n    deletePasskey: string;\n    deleteAccount: string;\n    selectLoginMethodForFutureLogins: string;\n    howDoYouWantToLogin: string;\n    deleteSecurityKey: string;\n    renameSecurityKey: string;\n  };\n  labels: {\n    authenticatorApp: string;\n    authenticatorAppAdd: string;\n    authenticatorAppManage: string;\n    or: string;\n    no: string;\n    yes: string;\n    email: string;\n    continue: string;\n    copied: string;\n    configured: string;\n    skip: string;\n    save: string;\n    password: string;\n    passkey: string;\n    passcode: string;\n    signInPassword: string;\n    signInPasscode: string;\n    forgotYourPassword: string;\n    back: string;\n    registerAuthenticator: string;\n    securityKey: string;\n    securityKeyUse: string;\n    signIn: string;\n    signInPasskey: string;\n    signUp: string;\n    sendNewPasscode: string;\n    staySignedIn: string;\n    trustDevice: string;\n    passwordRetryAfter: string;\n    passcodeResendAfter: string;\n    useAnotherMethod: string;\n    unverifiedEmail: string;\n    primaryEmail: string;\n    setAsPrimaryEmail: string;\n    verify: string;\n    delete: string;\n    newEmailAddress: string;\n    newPassword: string;\n    rename: string;\n    newPasskeyName: string;\n    addEmail: string;\n    createPasskey: string;\n    webauthnUnsupported: string;\n    signInWith: string;\n    deleteAccount: string;\n    emailOrUsername: string;\n    username: string;\n    optional: string;\n    dontHaveAnAccount: string;\n    alreadyHaveAnAccount: string;\n    changePassword: string;\n    setPassword: string;\n    changeUsername: string;\n    setUsername: string;\n    revoke: string;\n    currentSession: string;\n    newSecurityKeyName: string;\n    createSecurityKey: string;\n    lastUsed: string;\n    connectAccount: string;\n  };\n  errors: {\n    somethingWentWrong: string;\n    requestTimeout: string;\n    invalidPassword: string;\n    invalidPasscode: string;\n    passcodeAttemptsReached: string;\n    tooManyRequests: string;\n    unauthorized: string;\n    invalidWebauthnCredential: string;\n    passcodeExpired: string;\n    userVerification: string;\n    emailAddressAlreadyExistsError: string;\n    maxNumOfEmailAddressesReached: string;\n    thirdPartyAccessDenied: string;\n    thirdPartyMultipleAccounts: string;\n    thirdPartyUnverifiedEmail: string;\n    signupDisabled: string;\n    handlerNotFoundError: string;\n  };\n  flowErrors: {\n    technical_error: string;\n    flow_expired_error: string;\n    value_invalid_error: string;\n    passcode_invalid: string;\n    passkey_invalid: string;\n    passcode_max_attempts_reached: string;\n    rate_limit_exceeded: string;\n    unknown_username_error: string;\n    unknown_email_error: string;\n    username_already_exists: string;\n    invalid_username_error: string;\n    email_already_exists: string;\n    not_found: string;\n    flow_discontinuity_error: string;\n    operation_not_permitted_error: string;\n    platform_authenticator_required: string;\n    form_data_invalid_error: string;\n    unauthorized: string;\n    value_missing_error: string;\n    value_too_long_error: string;\n    value_too_short_error: string;\n    webauthn_credential_invalid_mfa_only: string;\n    webauthn_credential_already_exists: string;\n    third_party_access_denied: string;\n  };\n}\n\nexport const defaultTranslations: Translations = {\n  en,\n};\n"
  },
  {
    "path": "frontend/elements/src/i18n/zh.ts",
    "content": "import { Translation } from \"./translations\";\n\nexport const zh: Translation = {\n  headlines: {\n    error: \"发生错误\",\n    loginEmail: \"登录或注册\",\n    loginEmailNoSignup: \"登录\",\n    loginFinished: \"登录成功\",\n    loginPasscode: \"输入验证码\",\n    loginPassword: \"输入密码\",\n    registerAuthenticator: \"创建密钥\",\n    registerConfirm: \"创建账号？\",\n    registerPassword: \"设置新密码\",\n    otpSetUp: \"设置身份验证应用\",\n    profileEmails: \"电子邮件\",\n    profilePassword: \"密码\",\n    profilePasskeys: \"密钥\",\n    isPrimaryEmail: \"主要电子邮件地址\",\n    setPrimaryEmail: \"设定主要电子邮件地址\",\n    createEmail: \"输入新电子邮件\",\n    createUsername: \"输入新用户名\",\n    emailVerified: \"已验证\",\n    emailUnverified: \"未验证\",\n    emailDelete: \"删除\",\n    renamePasskey: \"重命名密钥\",\n    deletePasskey: \"删除密钥\",\n    lastUsedAt: \"最后使用在\",\n    createdAt: \"创建于\",\n    connectedAccounts: \"连接的账户\",\n    deleteAccount: \"删除帐户\",\n    accountNotFound: \"未找到帐户\",\n    signIn: \"登录\",\n    signUp: \"注册\",\n    selectLoginMethod: \"选择登录方法\",\n    setupLoginMethod: \"设置登录方法\",\n    lastUsed: \"最后一次查看\",\n    ipAddress: \"IP 地址\",\n    revokeSession: \"撤销会话\",\n    profileSessions: \"会话\",\n    mfaSetUp: \"设置 MFA\",\n    securityKeySetUp: \"添加安全密钥\",\n    securityKeyLogin: \"安全密钥\",\n    otpLogin: \"验证码\",\n    renameSecurityKey: \"重命名安全密钥\",\n    deleteSecurityKey: \"删除安全密钥\",\n    securityKeys: \"安全密钥\",\n    authenticatorApp: \"身份验证应用\",\n    authenticatorAppAlreadySetUp: \"身份验证应用已设置\",\n    authenticatorAppNotSetUp: \"设置身份验证应用\",\n    trustDevice: \"信任此浏览器？\",\n    deleteIdentity: \"删除连接\",\n  },\n  texts: {\n    enterPasscode: \"请输入发送到您邮箱地址的验证码。\",\n    enterPasscodeNoEmail: \"输入发送到您的主要电子邮件地址的验证码。\",\n    setupPasskey:\n      \"使用密钥轻松安全地登录您的账户。注意：您的生物识别数据仅存储在您的设备上，永远不会与任何人共享。\",\n    createAccount: \"没有“{emailAddress}”的账户存在。你想要创建一个新账户吗?\",\n    otpEnterVerificationCode:\n      \"在下面输入从身份验证应用获取的一次性密码 (OTP)：\",\n    otpScanQRCode:\n      \"使用您的身份验证应用扫描二维码（例如 Google Authenticator 或任何其他 TOTP 应用）。另外，您也可以手动输入 OTP 秘密密钥到应用中。\",\n    otpSecretKey: \"OTP 秘密密钥\",\n    passwordFormatHint: \"必须长在{minLength}和{maxLength}字符之间。\",\n    setPrimaryEmail: \"将此电子邮件地址设置为用于联系您。\",\n    isPrimaryEmail: \"如有必要，此电子邮件地址将用于联系您。\",\n    emailVerified: \"此电子邮件地址已经过验证。\",\n    emailUnverified: \"此电子邮件地址尚未验证。\",\n    emailDelete:\n      \"如果您删除此电子邮件地址，将无法再用于登录您的账户。可能已经用此电子邮件地址创建的密钥将保持完整。\",\n    renamePasskey: \"为密钥设定名称，帮助您识别其所存储的位置。\",\n    deletePasskey:\n      \"从您的账户中删除此密钥。请注意，密钥将继续存在于您的设备上，也需要在那里被删除。\",\n    deleteAccount: \"您确定要删除此账号吗？所有数据将立即被删除，且无法恢复。\",\n    noAccountExists: '没有账户\"{emailAddress}\"。',\n    selectLoginMethodForFutureLogins:\n      \"请选择以下登录方法之一以供将来登录使用。\",\n    howDoYouWantToLogin: \"您想如何登录？\",\n    mfaSetUp:\n      \"通过多因素认证（MFA）保护您的账户。MFA 在您的登录过程中增加了额外的一步，确保即使您的密码或电子邮件账户被泄露，您的账户仍然安全。\",\n    securityKeyLogin:\n      \"连接或激活您的安全密钥，然后点击下面的按钮。准备好后，通过 USB、NFC 或手机使用它。按照提示完成登录过程。\",\n    otpLogin:\n      \"打开您的身份验证应用以获取一次性密码（OTP）。在下面的字段中输入代码以完成登录。\",\n    renameSecurityKey: \"为安全密钥设置名称。\",\n    deleteSecurityKey: \"从您的账户中删除此安全密钥。\",\n    authenticatorAppAlreadySetUp:\n      \"您的账户通过身份验证应用保护，该应用生成基于时间的一次性密码 (TOTP) 以实现多因素认证。\",\n    authenticatorAppNotSetUp:\n      \"使用生成基于时间的一次性密码 (TOTP) 的身份验证应用保护您的账户以实现多因素认证。\",\n    securityKeySetUp:\n      \"通过 USB、蓝牙或 NFC 使用专用安全密钥，或使用手机。连接或激活您的安全密钥，然后点击下面的按钮，按照提示完成注册。\",\n    trustDevice:\n      \"如果您信任此浏览器，下次登录时您无需输入一次性密码（OTP）或使用您的安全密钥进行多因素认证（MFA）。\",\n  },\n  labels: {\n    or: \"或\",\n    no: \"否\",\n    yes: \"是\",\n    email: \"电子邮件\",\n    continue: \"继续\",\n    copied: \"已复制\",\n    skip: \"跳过\",\n    save: \"保存\",\n    passkey: \"密码\",\n    passcode: \"访问码\",\n    password: \"密码\",\n    signInPassword: \"使用密码登录\",\n    signInPasscode: \"使用验证码登录\",\n    forgotYourPassword: \"忘记密码了吗？\",\n    back: \"返回\",\n    signInPasskey: \"使用密钥登录\",\n    registerAuthenticator: \"创建密钥\",\n    signIn: \"登录\",\n    signUp: \"注册\",\n    sendNewPasscode: \"发送新代码\",\n    passwordRetryAfter: \"{passwordRetryAfter}后重试\",\n    passcodeResendAfter: \"{passcodeResendAfter}后请求新的代码\",\n    unverifiedEmail: \"未验证\",\n    primaryEmail: \"主要的\",\n    setAsPrimaryEmail: \"设为主要\",\n    verify: \"验证\",\n    delete: \"删除\",\n    newEmailAddress: \"新电子邮件地址\",\n    newPassword: \"新密码\",\n    rename: \"重命名\",\n    newPasskeyName: \"新密钥名称\",\n    addEmail: \"添加电子邮件\",\n    createPasskey: \"创建密钥\",\n    webauthnUnsupported: \"您的浏览器不支持密钥\",\n    signInWith: \"通过 {provider} 登录\",\n    deleteAccount: \"是的，删除此帐户。\",\n    emailOrUsername: \"电子邮件或用户名\",\n    username: \"用户名\",\n    optional: \"可选的\",\n    dontHaveAnAccount: \"没有账号？\",\n    alreadyHaveAnAccount: \"已有账号？\",\n    changeUsername: \"更改用户名\",\n    setUsername: \"设置用户名\",\n    changePassword: \"更改密码\",\n    setPassword: \"设置密码\",\n    revoke: \"撤销\",\n    currentSession: \"当前会话\",\n    authenticatorApp: \"身份验证应用\",\n    securityKey: \"安全密钥\",\n    securityKeyUse: \"使用安全密钥\",\n    newSecurityKeyName: \"新安全密钥名称\",\n    createSecurityKey: \"添加安全密钥\",\n    authenticatorAppManage: \"管理身份验证应用\",\n    authenticatorAppAdd: \"设置\",\n    configured: \"已配置\",\n    useAnotherMethod: \"使用其他方法\",\n    lastUsed: \"最后使用\",\n    trustDevice: \"信任此浏览器\",\n    staySignedIn: \"保持登录状态\",\n    connectAccount: \"连接账户\",\n  },\n  errors: {\n    somethingWentWrong: \"发生技术错误。请稍后再试。\",\n    requestTimeout: \"请求超时。\",\n    invalidPassword: \"邮箱或密码错误。\",\n    invalidPasscode: \"提供的验证码不正确。\",\n    passcodeAttemptsReached: \"验证码输入次数过多。请请求新的验证码。\",\n    tooManyRequests: \"请求太频繁。请稍后重试。\",\n    unauthorized: \"您的会话已过期。请再次登录。\",\n    invalidWebauthnCredential: \"此密钥已无法使用。\",\n    passcodeExpired: \"验证码已过期。请请求新的验证码。\",\n    userVerification:\n      \"需要用户验证。请确保你的验证设备已经用PIN或生物识别保护。\",\n    emailAddressAlreadyExistsError: \"电子邮件地址已存在。\",\n    maxNumOfEmailAddressesReached: \"不能添加更多的电子邮件地址。\",\n    thirdPartyAccessDenied:\n      \"访问被拒绝。该请求已被用户取消或者供应商由于其他原因拒绝了访问。\",\n    thirdPartyMultipleAccounts: \"无法确定账户。电子邮件地址被多个账户使用。\",\n    thirdPartyUnverifiedEmail:\n      \"需要电子邮件验证。请与您的提供商验证使用的电子邮件地址。\",\n    signupDisabled: \"帐户注册被禁用。\",\n    handlerNotFoundError:\n      \"当前步骤在此应用程序版本中不受支持。请稍后再试，或联系支持团队以获取帮助。\",\n  },\n  flowErrors: {\n    technical_error: \"发生技术错误。请稍后再试。\",\n    flow_expired_error: \"会话已过期，请点击按钮重新启动。\",\n    value_invalid_error: \"输入的值无效。\",\n    passcode_invalid: \"提供的密码不正确。\",\n    passkey_invalid: \"此密码无法再使用。\",\n    passcode_max_attempts_reached:\n      \"密码输入错误次数太多。请请求一个新的验证码。\",\n    rate_limit_exceeded: \"请求过多。请等待重复所请求的操作。\",\n    unknown_username_error: \"用户名未知。\",\n    unknown_email_error: \"电子邮件地址未知。\",\n    username_already_exists: \"用户名已被使用。\",\n    invalid_username_error: \"用户名只能包含字母、数字和下划线。\",\n    email_already_exists: \"电子邮件已被使用。\",\n    not_found: \"未找到请求的资源。\",\n    operation_not_permitted_error: \"不允许此操作。\",\n    flow_discontinuity_error: \"由于用户设置或提供商配置，流程无法继续。\",\n    form_data_invalid_error: \"提交的表单数据包含错误。\",\n    unauthorized: \"您的会话已过期。请再次登录。\",\n    value_missing_error: \"值丢失。\",\n    value_too_long_error: \"值太长。\",\n    value_too_short_error: \"值太短。\",\n    webauthn_credential_invalid_mfa_only:\n      \"此凭证仅可作为第二因素安全密钥使用。\",\n    webauthn_credential_already_exists:\n      \"请求已超时、被取消或设备已注册。请重试或尝试使用其他设备。\",\n    platform_authenticator_required:\n      \"您的账户配置为使用平台身份验证器，但您当前的设备或浏览器不支持该功能。请尝试使用兼容的设备或浏览器重新进行尝试。\",\n    third_party_access_denied:\n      \"访问被拒绝。该请求已被用户取消或者供应商由于其他原因拒绝了访问。\",\n  },\n};\n"
  },
  {
    "path": "frontend/elements/src/index.ts",
    "content": "export * from \"./Elements\";\nexport * from \"@teamhanko/hanko-frontend-sdk\";\nimport { Translation } from \"./i18n/translations\";\nexport type { Translation };\n"
  },
  {
    "path": "frontend/elements/src/pages/CreateEmailPage.tsx",
    "content": "import { useContext, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Input from \"../components/form/Input\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Link from \"../components/link/Link\";\n\ntype Props = {\n  state: State<\"onboarding_email\">;\n};\n\nconst CreateEmailPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const [email, setEmail] = useState<string>();\n\n  const onEmailInput = async (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      setEmail(event.target.value);\n    }\n  };\n\n  const onEmailSubmit = async (event: Event) => {\n    event.preventDefault();\n    return flowState.actions.email_address_set.run({ email });\n  };\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.createEmail\")}</Headline1>\n        <ErrorBox state={flowState} />\n        <Form\n          onSubmit={onEmailSubmit}\n          flowAction={flowState.actions.email_address_set}\n        >\n          <Input\n            type={\"email\"}\n            autoComplete={\"email\"}\n            autoCorrect={\"off\"}\n            flowInput={flowState.actions.email_address_set.inputs.email}\n            onInput={onEmailInput}\n            placeholder={t(\"labels.email\")}\n            pattern={\"^.*[^0-9]+$\"}\n            value={email}\n          />\n          <Button>{t(\"labels.continue\")}</Button>\n        </Form>\n      </Content>\n      <Footer hidden={!flowState.actions.skip.enabled}>\n        <span hidden />\n        <Link\n          flowAction={flowState.actions.skip}\n          loadingSpinnerPosition={\"left\"}\n        >\n          {t(\"labels.skip\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default CreateEmailPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/CreateOTPSecretPage.tsx",
    "content": "import { useCallback, useContext, useEffect, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Button from \"../components/form/Button\";\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Footer from \"../components/wrapper/Footer\";\nimport CodeInput from \"../components/form/CodeInput\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Link from \"../components/link/Link\";\nimport OTPCreationDetails from \"../components/otp/OTPCreationDetails\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ninterface Props {\n  state: State<\"mfa_otp_secret_creation\">;\n}\n\nconst CreateOTPSecretPage = (props: Props) => {\n  const numberOfDigits = 6;\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const [passcodeDigits, setPasscodeDigits] = useState<string[]>([]);\n\n  const submitPasscode = useCallback(\n    async (code: string) => {\n      return flowState.actions.otp_code_verify.run({\n        otp_code: code,\n      });\n    },\n    [flowState],\n  );\n\n  const onPasscodeInput = (digits: string[]) => {\n    setPasscodeDigits(digits);\n    // Automatically submit the Passcode when every input contains a digit.\n    if (digits.filter((digit) => digit !== \"\").length === numberOfDigits) {\n      return submitPasscode(digits.join(\"\"));\n    }\n  };\n\n  const onPasscodeSubmit = async (event: Event) => {\n    event.preventDefault();\n    return submitPasscode(passcodeDigits.join(\"\"));\n  };\n\n  useEffect(() => {\n    if (flowState.error?.code === \"passcode_invalid\") setPasscodeDigits([]);\n  }, [flowState]);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(`headlines.otpSetUp`)}</Headline1>\n        <ErrorBox state={flowState} />\n        <Paragraph>{t(\"texts.otpScanQRCode\")}</Paragraph>\n        <OTPCreationDetails\n          src={flowState.payload.otp_image_source}\n          secret={flowState.payload.otp_secret}\n        />\n        <Paragraph>{t(\"texts.otpEnterVerificationCode\")}</Paragraph>\n        <Form\n          flowAction={flowState.actions.otp_code_verify}\n          onSubmit={onPasscodeSubmit}\n        >\n          <CodeInput\n            onInput={onPasscodeInput}\n            passcodeDigits={passcodeDigits}\n            numberOfInputs={numberOfDigits}\n          />\n          <Button>{t(\"labels.continue\")}</Button>\n        </Form>\n      </Content>\n      <Footer>\n        <Link\n          flowAction={flowState.actions.back}\n          loadingSpinnerPosition={\"right\"}\n        >\n          {t(\"labels.back\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default CreateOTPSecretPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/CreatePasswordPage.tsx",
    "content": "import { useContext, useState } from \"preact/compat\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Input from \"../components/form/Input\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Link from \"../components/link/Link\";\n\ntype Props = {\n  state: State<\"password_creation\">;\n};\n\nconst CreatePasswordPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const [password, setPassword] = useState<string>();\n\n  const onPasswordInput = async (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      setPassword(event.target.value);\n    }\n  };\n\n  const onPasswordSubmit = async (event: Event) => {\n    event.preventDefault();\n    return flowState.actions.register_password.run({ new_password: password });\n  };\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.registerPassword\")}</Headline1>\n        <ErrorBox state={flowState} />\n        <Paragraph>\n          {t(\"texts.passwordFormatHint\", {\n            minLength:\n              flowState.actions.register_password.inputs.new_password\n                .min_length,\n            maxLength: 72,\n          })}\n        </Paragraph>\n        <Form\n          flowAction={flowState.actions.register_password}\n          onSubmit={onPasswordSubmit}\n        >\n          <Input\n            type={\"password\"}\n            autocomplete={\"new-password\"}\n            flowInput={flowState.actions.register_password.inputs.new_password}\n            placeholder={t(\"labels.newPassword\")}\n            onInput={onPasswordInput}\n            autofocus\n          />\n          <Button>{t(\"labels.continue\")}</Button>\n        </Form>\n      </Content>\n      <Footer\n        hidden={\n          !flowState.actions.back.enabled && !flowState.actions.skip.enabled\n        }\n      >\n        <Link\n          loadingSpinnerPosition={\"right\"}\n          flowAction={flowState.actions.back}\n        >\n          {t(\"labels.back\")}\n        </Link>\n        <Link\n          loadingSpinnerPosition={\"left\"}\n          flowAction={flowState.actions.skip}\n        >\n          {t(\"labels.skip\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default CreatePasswordPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/CreateSecurityKeyPage.tsx",
    "content": "\nimport { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport Link from \"../components/link/Link\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ninterface Props {\n  state: State<\"mfa_security_key_creation\">;\n}\n\nconst CreateSecurityKeyPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.securityKeySetUp\")}</Headline1>\n        <ErrorBox state={flowState} />\n        <Paragraph>{t(\"texts.securityKeySetUp\")}</Paragraph>\n        <Form flowAction={flowState.actions.webauthn_generate_creation_options}>\n          <Button autofocus icon={\"securityKey\"}>\n            {t(\"labels.createSecurityKey\")}\n          </Button>\n        </Form>\n      </Content>\n      <Footer hidden={!flowState.actions.back.enabled}>\n        <Link\n          loadingSpinnerPosition={\"right\"}\n          flowAction={flowState.actions.back}\n        >\n          {t(\"labels.back\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default CreateSecurityKeyPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/CreateUsernamePage.tsx",
    "content": "import { useContext, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Input from \"../components/form/Input\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Link from \"../components/link/Link\";\n\ntype Props = {\n  state: State<\"onboarding_username\">;\n};\n\nconst CreateUsernamePage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const [username, setUsername] = useState<string>();\n\n  const onUsernameInput = async (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      setUsername(event.target.value);\n    }\n  };\n\n  const onUsernameSubmit = async (event: Event) => {\n    event.preventDefault();\n    return flowState.actions.username_create.run({ username });\n  };\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.createUsername\")}</Headline1>\n        <ErrorBox state={flowState} />\n        <Form\n          flowAction={flowState.actions.username_create}\n          onSubmit={onUsernameSubmit}\n        >\n          <Input\n            type={\"text\"}\n            autoComplete={\"username\"}\n            autoCorrect={\"off\"}\n            flowInput={flowState.actions.username_create.inputs.username}\n            onInput={onUsernameInput}\n            value={username}\n            placeholder={t(\"labels.username\")}\n          />\n          <Button>{t(\"labels.continue\")}</Button>\n        </Form>\n      </Content>\n      <Footer hidden={!flowState.actions.skip.enabled}>\n        <span hidden />\n        <Link\n          flowAction={flowState.actions.skip}\n          loadingSpinnerPosition={\"left\"}\n        >\n          {t(\"labels.skip\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default CreateUsernamePage;\n"
  },
  {
    "path": "frontend/elements/src/pages/CredentialOnboardingChooser.tsx",
    "content": "import { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Link from \"../components/link/Link\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\n\ninterface Props {\n  state: State<\"credential_onboarding_chooser\">;\n}\n\nconst CredentialOnboardingChooserPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.setupLoginMethod\")}</Headline1>\n        <ErrorBox flowError={flowState?.error} />\n        <Paragraph>{t(\"texts.selectLoginMethodForFutureLogins\")}</Paragraph>\n        <Form flowAction={flowState.actions.continue_to_passkey_registration}>\n          <Button secondary icon={\"passkey\"}>\n            {t(\"labels.passkey\")}\n          </Button>\n        </Form>\n        <Form flowAction={flowState.actions.continue_to_password_registration}>\n          <Button secondary icon={\"password\"}>\n            {t(\"labels.password\")}\n          </Button>\n        </Form>\n      </Content>\n      <Footer\n        hidden={\n          !flowState.actions.back.enabled && !flowState.actions.skip.enabled\n        }\n      >\n        <Link\n          loadingSpinnerPosition={\"right\"}\n          flowAction={flowState.actions.back}\n        >\n          {t(\"labels.back\")}\n        </Link>\n        <Link\n          loadingSpinnerPosition={\"left\"}\n          flowAction={flowState.actions.skip}\n        >\n          {t(\"labels.skip\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default CredentialOnboardingChooserPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/DeleteAccountPage.tsx",
    "content": "import { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport Footer from \"../components/wrapper/Footer\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Link from \"../components/link/Link\";\nimport Checkbox from \"../components/form/Checkbox\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\ninterface Props {\n  state: State<\"profile_init\">;\n  onBack: (event: Event) => Promise<void>;\n}\n\nconst DeleteAccountPage = ({ state, onBack }: Props) => {\n  const { t } = useContext(TranslateContext);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.deleteAccount\")}</Headline1>\n        <ErrorBox flowError={null} />\n        <Paragraph>{t(\"texts.deleteAccount\")}</Paragraph>\n        <Form flowAction={state.actions.account_delete}>\n          <Checkbox\n            required={true}\n            type={\"checkbox\"}\n            label={t(\"labels.deleteAccount\")}\n          />\n          <Button>{t(\"labels.delete\")}</Button>\n        </Form>\n      </Content>\n      <Footer>\n        <Link onClick={onBack}>{t(\"labels.back\")}</Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default DeleteAccountPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/DeviceTrustPage.tsx",
    "content": "import { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Link from \"../components/link/Link\";\n\ninterface Props {\n  state: State<\"device_trust\">;\n}\n\nconst DeviceTrustPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.trustDevice\")}</Headline1>\n        <ErrorBox flowError={flowState?.error} />\n        <Paragraph>{t(\"texts.trustDevice\")}</Paragraph>\n        <Form flowAction={flowState.actions.trust_device}>\n          <Button>{t(\"labels.trustDevice\")}</Button>\n        </Form>\n      </Content>\n      <Footer>\n        <Link\n          flowAction={flowState.actions.back}\n          loadingSpinnerPosition={\"right\"}\n        >\n          {t(\"labels.back\")}\n        </Link>\n        <Link\n          flowAction={flowState.actions.skip}\n          loadingSpinnerPosition={\"left\"}\n        >\n          {t(\"labels.skip\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default DeviceTrustPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/EditPasswordPage.tsx",
    "content": "import { useContext, useState } from \"preact/compat\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Input from \"../components/form/Input\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ntype Props = {\n  state: State<\"login_password_recovery\">;\n};\n\nconst EditPasswordPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const [password, setPassword] = useState<string>();\n\n  const onPasswordInput = async (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      setPassword(event.target.value);\n    }\n  };\n\n  const onPasswordSubmit = async (event: Event) => {\n    event.preventDefault();\n    return flowState.actions.password_recovery.run({ new_password: password });\n  };\n\n  return (\n    <Content>\n      <Headline1>{t(\"headlines.registerPassword\")}</Headline1>\n      <ErrorBox state={flowState} />\n      <Paragraph>\n        {t(\"texts.passwordFormatHint\", {\n          minLength:\n            flowState.actions.password_recovery.inputs.new_password.min_length,\n          maxLength: 72,\n        })}\n      </Paragraph>\n      <Form\n        flowAction={flowState.actions.password_recovery}\n        onSubmit={onPasswordSubmit}\n      >\n        <Input\n          type={\"password\"}\n          autocomplete={\"new-password\"}\n          flowInput={flowState.actions.password_recovery.inputs.new_password}\n          placeholder={t(\"labels.newPassword\")}\n          onInput={onPasswordInput}\n          autofocus\n        />\n        <Button>{t(\"labels.continue\")}</Button>\n      </Form>\n    </Content>\n  );\n};\n\nexport default EditPasswordPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/ErrorPage.tsx",
    "content": "import { useCallback, useContext, useEffect, useState } from \"preact/compat\";\nimport { HankoError, State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { AppContext } from \"../contexts/AppProvider\";\n\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport Content from \"../components/wrapper/Content\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ninterface Props {\n  state?: State<any>;\n  error?: HankoError;\n}\n\nconst ErrorPage = ({ state, error }: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { init, componentName } = useContext(AppContext);\n  const [isLoading, setIsLoading] = useState<boolean>(false);\n  const retry = useCallback(() => init(componentName), [componentName, init]);\n\n  const { flowState } = useFlowState(state);\n\n  const onContinueClick = (event: Event) => {\n    event.preventDefault();\n    setIsLoading(true);\n    retry();\n  };\n\n  useEffect(() => {\n    addEventListener(\"hankoAuthSuccess\", retry);\n    return () => {\n      removeEventListener(\"hankoAuthSuccess\", retry);\n    };\n  }, [retry]);\n\n  return (\n    <Content>\n      <Headline1>{t(\"headlines.error\")}</Headline1>\n      <ErrorBox state={flowState} error={error} />\n      <Form onSubmit={onContinueClick}>\n        <Button isLoading={isLoading}>{t(\"labels.continue\")}</Button>\n      </Form>\n    </Content>\n  );\n};\n\nexport default ErrorPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/InitPage.tsx",
    "content": "import LoadingSpinner from \"../components/icons/LoadingSpinner\";\n\nconst InitPage = () => {\n  return <LoadingSpinner isLoading />;\n};\n\nexport default InitPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/LoginInitPage.tsx",
    "content": "import { useContext, useEffect, useMemo, useState } from \"preact/compat\";\nimport {\n  HankoError,\n  State,\n  WebauthnSupport,\n  setStoredCodeVerifier,\n  generateCodeVerifier,\n  clearStoredCodeVerifier,\n} from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { AppContext } from \"../contexts/AppProvider\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\nimport Button from \"../components/form/Button\";\nimport Input from \"../components/form/Input\";\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Divider from \"../components/spacer/Divider\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Link from \"../components/link/Link\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Checkbox from \"../components/form/Checkbox\";\nimport Spacer from \"../components/spacer/Spacer\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\n\ninterface Props {\n  state: State<\"login_init\">;\n}\n\ntype IdentifierTypes = \"username\" | \"email\" | \"identifier\";\n\nconst LoginInitPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const {\n    init,\n    initialComponentName,\n    uiState,\n    setUIState,\n    hidePasskeyButtonOnLogin,\n    lastLogin,\n  } = useContext(AppContext);\n\n  const [isFlowSwitchLoading, setIsFlowSwitchLoading] =\n    useState<boolean>(false);\n  const [identifierType, setIdentifierType] = useState<IdentifierTypes>(null);\n  const [identifier, setIdentifier] = useState<string>(null);\n  const { flowState } = useFlowState(props.state);\n  const isWebAuthnSupported = WebauthnSupport.supported();\n  const [thirdPartyError, setThirdPartyError] = useState<\n    HankoError | undefined\n  >(undefined);\n  const [selectedThirdPartyProvider, setSelectedThirdPartyProvider] = useState<\n    string | null\n  >(null);\n  const [rememberMe, setRememberMe] = useState<boolean>(false);\n\n  const onIdentifierInput = (event: Event) => {\n    event.preventDefault();\n    if (event.target instanceof HTMLInputElement) {\n      const { value } = event.target;\n      setIdentifier(value);\n      setIdentifierToUIState(value);\n    }\n  };\n\n  const onEmailSubmit = async (event: Event) => {\n    event.preventDefault();\n    setIdentifierToUIState(identifier);\n    return flowState.actions.continue_with_login_identifier.run({\n      [identifierType]: identifier,\n    });\n  };\n\n  const onRegisterClick = async (event: Event) => {\n    event.preventDefault();\n    setIsFlowSwitchLoading(true);\n    init(\"registration\");\n  };\n\n  const onRememberMeChange = async (event: Event) => {\n    setRememberMe((prev) => !prev);\n    return flowState.actions.remember_me.run({ remember_me: !rememberMe });\n  };\n\n  const setIdentifierToUIState = (value: string) => {\n    const setEmail = () =>\n      setUIState((prev) => ({ ...prev, email: value, username: null }));\n    const setUsername = () =>\n      setUIState((prev) => ({ ...prev, email: null, username: value }));\n    switch (identifierType) {\n      case \"email\":\n        setEmail();\n        break;\n      case \"username\":\n        setUsername();\n        break;\n      case \"identifier\":\n        if (value.match(/^[^@]+@[^@]+\\.[^@]+$/)) {\n          setEmail();\n        } else {\n          setUsername();\n        }\n        break;\n    }\n  };\n\n  const onThirdpartySubmit = async (event: Event, name: string) => {\n    event.preventDefault();\n    setSelectedThirdPartyProvider(name);\n\n    const codeVerifier = generateCodeVerifier();\n    setStoredCodeVerifier(codeVerifier);\n\n    try {\n      const nextState = await flowState.actions.thirdparty_oauth.run({\n        provider: name,\n        redirect_to: window.location.toString(),\n        code_verifier: codeVerifier,\n      });\n\n      if (nextState.error) {\n        clearStoredCodeVerifier();\n        setSelectedThirdPartyProvider(null);\n      }\n\n      return nextState;\n    } catch (e) {\n      clearStoredCodeVerifier();\n      setSelectedThirdPartyProvider(null);\n      throw e;\n    }\n  };\n\n  const showDivider = useMemo(\n    () =>\n      (!!flowState.actions.webauthn_generate_request_options.enabled ||\n        !!flowState.actions.thirdparty_oauth.enabled) &&\n      flowState.actions.continue_with_login_identifier.enabled,\n    [flowState.actions],\n  );\n\n  const inputs = flowState.actions.continue_with_login_identifier.inputs;\n\n  useEffect(() => {\n    const inputs = flowState.actions.continue_with_login_identifier.inputs;\n\n    if (inputs?.email) {\n      setIdentifierType(\"email\");\n      setIdentifier(uiState.email);\n    } else if (inputs?.username) {\n      setIdentifierType(\"username\");\n      setIdentifier(uiState.username);\n    } else {\n      setIdentifierType(\"identifier\");\n      setIdentifier(uiState.email || uiState.username);\n    }\n  }, [flowState, uiState.email, uiState.username]);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.signIn\")}</Headline1>\n        <ErrorBox state={flowState} error={thirdPartyError} />\n        {inputs ? (\n          <>\n            <Form\n              flowAction={flowState.actions.continue_with_login_identifier}\n              onSubmit={onEmailSubmit}\n              maxWidth\n            >\n              {inputs.email ? (\n                <Input\n                  type={\"email\"}\n                  autoComplete={\"username webauthn\"}\n                  autoCorrect={\"off\"}\n                  flowInput={inputs.email}\n                  onInput={onIdentifierInput}\n                  value={identifier}\n                  placeholder={t(\"labels.email\")}\n                  pattern={\"^[^@]+@[^@]+\\\\.[^@]+$\"}\n                />\n              ) : inputs.username ? (\n                <Input\n                  type={\"text\"}\n                  autoComplete={\"username webauthn\"}\n                  autoCorrect={\"off\"}\n                  flowInput={inputs.username}\n                  onInput={onIdentifierInput}\n                  value={identifier}\n                  placeholder={t(\"labels.username\")}\n                />\n              ) : (\n                <Input\n                  type={\"text\"}\n                  autoComplete={\"username webauthn\"}\n                  autoCorrect={\"off\"}\n                  flowInput={inputs.identifier}\n                  onInput={onIdentifierInput}\n                  value={identifier}\n                  placeholder={t(\"labels.emailOrUsername\")}\n                />\n              )}\n              <Button>{t(\"labels.continue\")}</Button>\n            </Form>\n            <Divider hidden={!showDivider}>{t(\"labels.or\")}</Divider>\n          </>\n        ) : null}\n        {flowState.actions.thirdparty_oauth.enabled\n          ? flowState.actions.thirdparty_oauth.inputs.provider.allowed_values?.map(\n              (v) => {\n                return (\n                  <Form\n                    key={v.value}\n                    flowAction={flowState.actions.thirdparty_oauth}\n                    onSubmit={(event) => onThirdpartySubmit(event, v.value)}\n                  >\n                    <Button\n                      isLoading={v.value == selectedThirdPartyProvider}\n                      secondary\n                      // @ts-ignore\n                      icon={\n                        v.value.startsWith(\"custom_\")\n                          ? \"customProvider\"\n                          : v.value\n                      }\n                      showLastUsed={\n                        lastLogin?.login_method == \"third_party\" &&\n                        lastLogin?.third_party_provider == v.value\n                      }\n                    >\n                      {t(\"labels.signInWith\", { provider: v.name })}\n                    </Button>\n                  </Form>\n                );\n              },\n            )\n          : null}\n        {flowState.actions.webauthn_generate_request_options.enabled &&\n        !hidePasskeyButtonOnLogin ? (\n          <Form\n            flowAction={flowState.actions.webauthn_generate_request_options}\n          >\n            <Button\n              secondary\n              title={\n                !isWebAuthnSupported ? t(\"labels.webauthnUnsupported\") : null\n              }\n              disabled={!isWebAuthnSupported}\n            >\n              {t(\"labels.signInPasskey\")}\n            </Button>\n          </Form>\n        ) : null}\n        {flowState.actions.remember_me.enabled && (\n          <>\n            <Spacer />\n            <Checkbox\n              required={false}\n              type={\"checkbox\"}\n              label={t(\"labels.staySignedIn\")}\n              checked={rememberMe}\n              onChange={onRememberMeChange}\n            />\n          </>\n        )}\n      </Content>\n      <Footer hidden={initialComponentName !== \"auth\"}>\n        <Paragraph center>\n          <span>{t(\"labels.dontHaveAnAccount\")}</span>\n          <Link\n            onClick={onRegisterClick}\n            loadingSpinnerPosition={\"left\"}\n            isLoading={isFlowSwitchLoading}\n          >\n            {t(\"labels.signUp\")}\n          </Link>\n        </Paragraph>\n      </Footer>\n    </>\n  );\n};\n\nexport default LoginInitPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/LoginMethodChooser.tsx",
    "content": "import { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Link from \"../components/link/Link\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\n\ninterface Props {\n  state: State<\"login_method_chooser\">;\n}\n\nconst LoginMethodChooserPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.selectLoginMethod\")}</Headline1>\n        <ErrorBox flowError={flowState?.error} />\n        <Paragraph>{t(\"texts.howDoYouWantToLogin\")}</Paragraph>\n        <Form flowAction={flowState.actions.continue_to_passcode_confirmation}>\n          <Button secondary icon={\"mail\"}>\n            {t(\"labels.passcode\")}\n          </Button>\n        </Form>\n        <Form flowAction={flowState.actions.continue_to_password_login}>\n          <Button secondary icon={\"password\"}>\n            {t(\"labels.password\")}\n          </Button>\n        </Form>\n        <Form flowAction={flowState.actions.webauthn_generate_request_options}>\n          <Button secondary={true} icon={\"passkey\"}>\n            {t(\"labels.passkey\")}\n          </Button>\n        </Form>\n      </Content>\n      <Footer>\n        <Link\n          flowAction={flowState.actions.back}\n          loadingSpinnerPosition={\"right\"}\n        >\n          {t(\"labels.back\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default LoginMethodChooserPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/LoginOTPPage.tsx",
    "content": "import { useCallback, useContext, useEffect, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Button from \"../components/form/Button\";\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Footer from \"../components/wrapper/Footer\";\nimport CodeInput from \"../components/form/CodeInput\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Link from \"../components/link/Link\";\n\ninterface Props {\n  state: State<\"login_otp\">;\n}\n\nconst LoginOTPPAge = (props: Props) => {\n  const numberOfDigits = 6;\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const [passcodeDigits, setPasscodeDigits] = useState<string[]>([]);\n\n  const submitPasscode = useCallback(\n    async (code: string) => {\n      return flowState.actions.otp_code_validate.run({ otp_code: code });\n    },\n    [flowState],\n  );\n\n  const onPasscodeInput = (digits: string[]) => {\n    setPasscodeDigits(digits);\n    // Automatically submit the Passcode when every input contains a digit.\n    if (digits.filter((digit) => digit !== \"\").length === numberOfDigits) {\n      return submitPasscode(digits.join(\"\"));\n    }\n  };\n\n  const onPasscodeSubmit = async (event: Event) => {\n    event.preventDefault();\n    return submitPasscode(passcodeDigits.join(\"\"));\n  };\n\n  useEffect(() => {\n    setPasscodeDigits([]);\n  }, [flowState]);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(`headlines.otpLogin`)}</Headline1>\n        <ErrorBox state={flowState} />\n        <Paragraph>{t(\"texts.otpLogin\")}</Paragraph>\n        <Form\n          flowAction={flowState.actions.otp_code_validate}\n          onSubmit={onPasscodeSubmit}\n        >\n          <CodeInput\n            onInput={onPasscodeInput}\n            passcodeDigits={passcodeDigits}\n            numberOfInputs={numberOfDigits}\n          />\n          <Button>{t(\"labels.continue\")}</Button>\n        </Form>\n      </Content>\n      <Footer\n        hidden={!flowState.actions.continue_to_login_security_key.enabled}\n      >\n        <Link\n          loadingSpinnerPosition={\"right\"}\n          flowAction={flowState.actions.continue_to_login_security_key}\n        >\n          {t(\"labels.useAnotherMethod\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default LoginOTPPAge;\n"
  },
  {
    "path": "frontend/elements/src/pages/LoginPasswordPage.tsx",
    "content": "import { useContext, useEffect, useMemo, useState } from \"preact/compat\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Form from \"../components/form/Form\";\nimport Input from \"../components/form/Input\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Link from \"../components/link/Link\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ntype Props = {\n  state: State<\"login_password\">;\n};\n\nconst LoginPasswordPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const [password, setPassword] = useState<string>();\n  const [passwordRetryAfter, setPasswordRetryAfter] = useState<number>();\n\n  const onPasswordInput = async (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      setPassword(event.target.value);\n    }\n  };\n\n  const onPasswordSubmit = async (event: Event) => {\n    event.preventDefault();\n    return flowState.actions.password_login.run({ password });\n  };\n\n  const recoveryLink = useMemo(\n    () => (\n      <Link\n        flowAction={\n          flowState.actions.continue_to_passcode_confirmation_recovery\n        }\n        loadingSpinnerPosition={\"left\"}\n      >\n        {t(\"labels.forgotYourPassword\")}\n      </Link>\n    ),\n    [flowState, t],\n  );\n\n  const loginMethodChooserLink = useMemo(\n    () => (\n      <Link\n        flowAction={flowState.actions.continue_to_login_method_chooser}\n        loadingSpinnerPosition={\"left\"}\n      >\n        {\"Choose another method\"}\n      </Link>\n    ),\n    [flowState],\n  );\n\n  // Count down the retry after countdown\n  useEffect(() => {\n    const timer =\n      passwordRetryAfter > 0 &&\n      setInterval(() => setPasswordRetryAfter(passwordRetryAfter - 1), 1000);\n\n    return () => clearInterval(timer);\n  }, [passwordRetryAfter]);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.loginPassword\")}</Headline1>\n        <ErrorBox state={flowState} />\n        <Form\n          flowAction={flowState.actions.password_login}\n          onSubmit={onPasswordSubmit}\n        >\n          <Input\n            type={\"password\"}\n            flowInput={flowState.actions.password_login.inputs.password}\n            autocomplete={\"current-password\"}\n            placeholder={t(\"labels.password\")}\n            onInput={onPasswordInput}\n            autofocus\n          />\n          <Button disabled={passwordRetryAfter > 0}>\n            {passwordRetryAfter > 0\n              ? t(\"labels.passwordRetryAfter\", { passwordRetryAfter })\n              : t(\"labels.signIn\")}\n          </Button>\n        </Form>\n        {flowState.actions.continue_to_login_method_chooser.enabled\n          ? recoveryLink\n          : null}\n      </Content>\n      <Footer>\n        <Link\n          flowAction={flowState.actions.back}\n          loadingSpinnerPosition={\"right\"}\n        >\n          {t(\"labels.back\")}\n        </Link>\n        {flowState.actions.continue_to_login_method_chooser.enabled\n          ? loginMethodChooserLink\n          : recoveryLink}\n      </Footer>\n    </>\n  );\n};\n\nexport default LoginPasswordPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/LoginSecurityKeyPage.tsx",
    "content": "import { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport Link from \"../components/link/Link\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ninterface Props {\n  state: State<\"login_security_key\">;\n}\n\nconst LoginSecurityKeyPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.securityKeyLogin\")}</Headline1>\n        <ErrorBox state={flowState} />\n        <Paragraph>{t(\"texts.securityKeyLogin\")}</Paragraph>\n        <Form flowAction={flowState.actions.webauthn_generate_request_options}>\n          <Button autofocus icon={\"securityKey\"}>\n            {t(\"labels.securityKeyUse\")}\n          </Button>\n        </Form>\n      </Content>\n      <Footer hidden={!flowState.actions.continue_to_login_otp.enabled}>\n        <Link\n          loadingSpinnerPosition={\"right\"}\n          flowAction={flowState.actions.continue_to_login_otp}\n        >\n          {t(\"labels.useAnotherMethod\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default LoginSecurityKeyPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/MFAMethodChooserPage.tsx",
    "content": "import { useContext, useMemo } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Link from \"../components/link/Link\";\n\ninterface Props {\n  state: State<\"mfa_method_chooser\">;\n}\n\nconst MFAMMethodChooserPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n\n  const singleAction = useMemo(() => {\n    const { actions } = flowState;\n\n    if (\n      actions.continue_to_security_key_creation.enabled &&\n      !actions.continue_to_otp_secret_creation.enabled\n    ) {\n      return actions.continue_to_security_key_creation;\n    }\n\n    if (\n      !actions.continue_to_security_key_creation.enabled &&\n      actions.continue_to_otp_secret_creation.enabled\n    ) {\n      return actions.continue_to_otp_secret_creation;\n    }\n\n    return undefined;\n  }, [flowState]);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.mfaSetUp\")}</Headline1>\n        <ErrorBox flowError={flowState?.error} />\n        <Paragraph>{t(\"texts.mfaSetUp\")}</Paragraph>\n        {singleAction ? (\n          <Form flowAction={singleAction}>\n            <Button>{t(\"labels.continue\")}</Button>\n          </Form>\n        ) : (\n          <>\n            <Form\n              flowAction={flowState.actions.continue_to_security_key_creation}\n            >\n              <Button secondary icon={\"securityKey\"}>\n                {t(\"labels.securityKey\")}\n              </Button>\n            </Form>\n            <Form\n              flowAction={flowState.actions.continue_to_otp_secret_creation}\n            >\n              <Button secondary icon={\"qrCodeScanner\"}>\n                {t(\"labels.authenticatorApp\")}\n              </Button>\n            </Form>\n          </>\n        )}\n      </Content>\n      <Footer>\n        <Link\n          loadingSpinnerPosition={\"right\"}\n          flowAction={flowState.actions.back}\n        >\n          {t(\"labels.back\")}\n        </Link>\n        <Link\n          loadingSpinnerPosition={\"left\"}\n          flowAction={flowState.actions.skip}\n        >\n          {t(\"labels.skip\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default MFAMMethodChooserPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/PasscodePage.tsx",
    "content": "import {\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n} from \"preact/compat\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { AppContext } from \"../contexts/AppProvider\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\n\nimport Button from \"../components/form/Button\";\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Footer from \"../components/wrapper/Footer\";\nimport CodeInput from \"../components/form/CodeInput\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Link from \"../components/link/Link\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ninterface Props {\n  state: State<\"passcode_confirmation\">;\n}\n\nconst PasscodePage = (props: Props) => {\n  const numberOfDigits = 6;\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n  const { uiState, setUIState } = useContext(AppContext);\n  const [ttl, setTtl] = useState<number>();\n  const [resendAfter, setResendAfter] = useState<number>(\n    flowState.payload.resend_after,\n  );\n  const [passcodeDigits, setPasscodeDigits] = useState<string[]>([]);\n\n  const maxAttemptsReached = useMemo(\n    () => flowState.error?.code === \"passcode_max_attempts_reached\",\n    [flowState],\n  );\n\n  const submitPasscode = useCallback(\n    async (code: string) => {\n      return await flowState.actions.verify_passcode.run({ code });\n    },\n    [flowState],\n  );\n\n  const onPasscodeInput = (digits: string[]) => {\n    setPasscodeDigits(digits);\n    // Automatically submit the Passcode when every input contains a digit.\n    if (digits.filter((digit) => digit !== \"\").length === numberOfDigits) {\n      return submitPasscode(digits.join(\"\"));\n    }\n  };\n\n  const onPasscodeSubmit = async (event: Event) => {\n    event.preventDefault();\n    return submitPasscode(passcodeDigits.join(\"\"));\n  };\n\n  useEffect(() => {\n    const timer = ttl > 0 && setInterval(() => setTtl(ttl - 1), 1000);\n    return () => clearInterval(timer);\n  }, [ttl]);\n\n  useEffect(() => {\n    const timer =\n      resendAfter > 0 &&\n      setInterval(() => {\n        setResendAfter(resendAfter - 1);\n      }, 1000);\n    return () => clearInterval(timer);\n  }, [resendAfter]);\n\n  useEffect(() => {\n    if (resendAfter == 0 && flowState.error?.code == \"rate_limit_exceeded\") {\n      setUIState((prev) => ({ ...prev, error: null }));\n    }\n  }, [resendAfter]);\n\n  useEffect(() => {\n    setPasscodeDigits([]);\n    if (flowState.payload.resend_after >= 0) {\n      setResendAfter(flowState.payload.resend_after);\n    }\n  }, [flowState]);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(`headlines.loginPasscode`)}</Headline1>\n        <ErrorBox state={flowState} />\n        <Paragraph>\n          {uiState.email\n            ? t(\"texts.enterPasscode\")\n            : t(\"texts.enterPasscodeNoEmail\")}\n        </Paragraph>\n        <Paragraph hidden={!uiState.email}>\n          <b>{uiState.email}</b>\n        </Paragraph>\n        <Form\n          flowAction={flowState.actions.verify_passcode}\n          onSubmit={onPasscodeSubmit}\n        >\n          <CodeInput\n            onInput={onPasscodeInput}\n            passcodeDigits={passcodeDigits}\n            numberOfInputs={numberOfDigits}\n            disabled={ttl <= 0 || maxAttemptsReached}\n          />\n          <Button disabled={ttl <= 0 || maxAttemptsReached}>\n            {t(\"labels.continue\")}\n          </Button>\n        </Form>\n      </Content>\n      <Footer>\n        <Link\n          flowAction={flowState.actions.back}\n          loadingSpinnerPosition={\"right\"}\n        >\n          {t(\"labels.back\")}\n        </Link>\n        <Link\n          disabled={resendAfter > 0}\n          flowAction={flowState.actions.resend_passcode}\n          loadingSpinnerPosition={\"left\"}\n        >\n          {resendAfter > 0\n            ? t(\"labels.passcodeResendAfter\", {\n                passcodeResendAfter: resendAfter,\n              })\n            : t(\"labels.sendNewPasscode\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default PasscodePage;\n"
  },
  {
    "path": "frontend/elements/src/pages/ProfilePage.tsx",
    "content": "import { useContext, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { useFlowState } from \"../hooks/UseFlowState\";\nimport { AppContext } from \"../contexts/AppProvider\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport ListEmailsAccordion from \"../components/accordion/ListEmailsAccordion\";\nimport ListWebauthnCredentialsAccordion from \"../components/accordion/ListWebauthnCredentialsAccordion\";\nimport AddEmailDropdown from \"../components/accordion/AddEmailDropdown\";\nimport ChangePasswordDropdown from \"../components/accordion/ChangePasswordDropdown\";\nimport AddWebauthnCredentialDropdown from \"../components/accordion/AddWebauthnCredentialDropdown\";\nimport Divider from \"../components/spacer/Divider\";\nimport Button from \"../components/form/Button\";\nimport Form from \"../components/form/Form\";\nimport Spacer from \"../components/spacer/Spacer\";\nimport ChangeUsernameDropdown from \"../components/accordion/ChangeUsernameDropdown\";\nimport DeleteAccountPage from \"./DeleteAccountPage\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport ListSessionsAccordion from \"../components/accordion/ListSessionsAccordion\";\nimport ManageAuthAppDropdown from \"../components/accordion/ManageAuthAppDropdown\";\nimport ListIdentities from \"../components/accordion/ListIdentities\";\nimport ConnectIdentityDropdown from \"../components/accordion/ConnectIdentityDropdown\";\n\ninterface Props {\n  state: State<\"profile_init\">;\n  enablePasskeys?: boolean;\n}\n\nconst ProfilePage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { setPage } = useContext(AppContext);\n  const { flowState } = useFlowState(props.state);\n\n  const [checkedItemID, setCheckedItemID] = useState<string>(\"\");\n\n  const animationFinished = () => {\n    return new Promise((resolve) => setTimeout(resolve, 360));\n  };\n\n  const onState = async (newState: State<any>) => {\n    if (!newState?.error) {\n      setCheckedItemID(null);\n      await animationFinished();\n    }\n\n    newState.dispatchAfterStateChangeEvent();\n  };\n\n  const onPasskeyDelete = async (event: Event, id: string) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.webauthn_credential_delete.run(\n      {\n        passkey_id: id,\n      },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const onWebauthnCredentialNameSubmit = async (\n    event: Event,\n    id: string,\n    name: string,\n  ) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.webauthn_credential_rename.run(\n      {\n        passkey_id: id,\n        passkey_name: name,\n      },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const onSecurityKeyDelete = async (event: Event, id: string) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.security_key_delete.run(\n      {\n        security_key_id: id,\n      },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    return onState(nextState);\n  };\n\n  const onBack = (event: Event) => {\n    event.preventDefault();\n    setPage(\n      <ProfilePage state={flowState} enablePasskeys={props.enablePasskeys} />,\n    );\n    return Promise.resolve();\n  };\n\n  const onUserDelete = (event: Event) => {\n    event.preventDefault();\n    setPage(<DeleteAccountPage onBack={onBack} state={flowState} />);\n    return Promise.resolve();\n  };\n\n  return (\n    <Content>\n      <ErrorBox\n        state={\n          flowState?.error?.code !== \"form_data_invalid_error\"\n            ? flowState\n            : null\n        }\n      />\n      {flowState.actions.username_create.enabled ||\n      flowState.actions.username_update.enabled ||\n      flowState.actions.username_delete.enabled ? (\n        <>\n          <Headline1>{t(\"labels.username\")}</Headline1>\n          {flowState.payload.user.username ? (\n            <Paragraph>\n              <b>{flowState.payload.user.username.username}</b>\n            </Paragraph>\n          ) : null}\n          <Paragraph>\n            {flowState.actions.username_create.enabled ||\n            flowState.actions.username_update.enabled ? (\n              <ChangeUsernameDropdown\n                onState={onState}\n                flowState={flowState}\n                checkedItemID={checkedItemID}\n                setCheckedItemID={setCheckedItemID}\n              />\n            ) : null}\n          </Paragraph>\n        </>\n      ) : null}\n      {flowState.payload?.user?.emails ||\n      flowState.actions.email_create.enabled ? (\n        <>\n          <Headline1>{t(\"headlines.profileEmails\")}</Headline1>\n          <Paragraph>\n            <ListEmailsAccordion\n              flowState={flowState}\n              onState={onState}\n              checkedItemID={checkedItemID}\n              setCheckedItemID={setCheckedItemID}\n            />\n            {flowState.actions.email_create.enabled ? (\n              <AddEmailDropdown\n                flowState={flowState}\n                onState={onState}\n                checkedItemID={checkedItemID}\n                setCheckedItemID={setCheckedItemID}\n              />\n            ) : null}\n          </Paragraph>\n        </>\n      ) : null}\n      {flowState.actions.password_create.enabled ||\n      flowState.actions.password_update.enabled ? (\n        <>\n          <Headline1>{t(\"headlines.profilePassword\")}</Headline1>\n          <Paragraph>\n            <ChangePasswordDropdown\n              flowState={flowState}\n              onState={onState}\n              checkedItemID={checkedItemID}\n              setCheckedItemID={setCheckedItemID}\n            />\n          </Paragraph>\n        </>\n      ) : null}\n      {props.enablePasskeys &&\n      (flowState.payload?.user?.passkeys ||\n        flowState.actions.webauthn_credential_create.enabled) ? (\n        <>\n          <Headline1>{t(\"headlines.profilePasskeys\")}</Headline1>\n          <Paragraph>\n            <ListWebauthnCredentialsAccordion\n              flowState={flowState}\n              onBack={onBack}\n              onCredentialNameSubmit={onWebauthnCredentialNameSubmit}\n              onCredentialDelete={onPasskeyDelete}\n              credentials={flowState.payload.user.passkeys}\n              checkedItemID={checkedItemID}\n              setCheckedItemID={setCheckedItemID}\n              allowCredentialDeletion={\n                !!flowState.actions.webauthn_credential_delete.enabled\n              }\n              credentialType={\"passkey\"}\n            />\n            {flowState.actions.webauthn_credential_create.enabled ? (\n              <AddWebauthnCredentialDropdown\n                flowState={flowState}\n                onState={onState}\n                credentialType={\"passkey\"}\n                checkedItemID={checkedItemID}\n                setCheckedItemID={setCheckedItemID}\n              />\n            ) : null}\n          </Paragraph>\n        </>\n      ) : null}\n      {flowState.payload.user.mfa_config?.security_keys_enabled ? (\n        <>\n          <Headline1>{t(\"headlines.securityKeys\")}</Headline1>\n          <Paragraph>\n            <ListWebauthnCredentialsAccordion\n              onBack={onBack}\n              flowState={flowState}\n              onCredentialNameSubmit={onWebauthnCredentialNameSubmit}\n              onCredentialDelete={onSecurityKeyDelete}\n              credentials={flowState.payload.user.security_keys}\n              checkedItemID={checkedItemID}\n              setCheckedItemID={setCheckedItemID}\n              allowCredentialDeletion={\n                !!flowState.actions.security_key_delete.enabled\n              }\n              credentialType={\"security-key\"}\n            />\n            {flowState.actions.security_key_create.enabled ? (\n              <AddWebauthnCredentialDropdown\n                flowState={flowState}\n                onState={onState}\n                credentialType={\"security-key\"}\n                checkedItemID={checkedItemID}\n                setCheckedItemID={setCheckedItemID}\n              />\n            ) : null}\n          </Paragraph>\n        </>\n      ) : null}\n      {flowState.payload.user.mfa_config?.totp_enabled ? (\n        <>\n          <Headline1>{t(\"headlines.authenticatorApp\")}</Headline1>\n          <Paragraph>\n            <ManageAuthAppDropdown\n              onState={onState}\n              flowState={flowState}\n              checkedItemID={checkedItemID}\n              setCheckedItemID={setCheckedItemID}\n            />\n          </Paragraph>\n        </>\n      ) : null}\n      {flowState.actions.connect_thirdparty_oauth_provider.enabled ||\n      flowState.actions.disconnect_thirdparty_oauth_provider.enabled ? (\n        <>\n          <Headline1>{t(\"headlines.connectedAccounts\")}</Headline1>\n          <ListIdentities\n            flowState={flowState}\n            onState={onState}\n            checkedItemID={checkedItemID}\n            setCheckedItemID={setCheckedItemID}\n          />\n          {flowState.actions.connect_thirdparty_oauth_provider.enabled ? (\n            <ConnectIdentityDropdown\n              setCheckedItemID={setCheckedItemID}\n              flowState={flowState}\n              onState={onState}\n              checkedItemID={checkedItemID}\n            />\n          ) : null}\n        </>\n      ) : null}\n      {flowState.payload.sessions ? (\n        <>\n          <Headline1>{t(\"headlines.profileSessions\")}</Headline1>\n          <Paragraph>\n            <ListSessionsAccordion\n              flowState={flowState}\n              onState={onState}\n              checkedItemID={checkedItemID}\n              setCheckedItemID={setCheckedItemID}\n            />\n          </Paragraph>\n        </>\n      ) : null}\n      {flowState.actions.account_delete.enabled ? (\n        <>\n          <Spacer />\n          <Paragraph>\n            <Divider />\n          </Paragraph>\n          <Paragraph>\n            <Form\n              onSubmit={onUserDelete}\n              flowAction={flowState.actions.account_delete}\n            >\n              <Button dangerous>{t(\"headlines.deleteAccount\")}</Button>\n            </Form>\n          </Paragraph>\n        </>\n      ) : null}\n    </Content>\n  );\n};\n\nexport default ProfilePage;\n"
  },
  {
    "path": "frontend/elements/src/pages/RegisterPasskeyPage.tsx",
    "content": "import { useContext } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\n\nimport Link from \"../components/link/Link\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\ninterface Props {\n  state: State<\"onboarding_create_passkey\">;\n}\n\nconst RegisterPasskeyPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { flowState } = useFlowState(props.state);\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.registerAuthenticator\")}</Headline1>\n        <ErrorBox state={flowState} />\n        <Paragraph>{t(\"texts.setupPasskey\")}</Paragraph>\n        <Form flowAction={flowState.actions.webauthn_generate_creation_options}>\n          <Button autofocus icon={\"passkey\"}>\n            {t(\"labels.registerAuthenticator\")}\n          </Button>\n        </Form>\n      </Content>\n      <Footer\n        hidden={\n          !flowState.actions.skip.enabled && !flowState.actions.back.enabled\n        }\n      >\n        <Link\n          loadingSpinnerPosition={\"right\"}\n          flowAction={flowState.actions.back}\n        >\n          {t(\"labels.back\")}\n        </Link>\n        <Link\n          loadingSpinnerPosition={\"left\"}\n          flowAction={flowState.actions.skip}\n        >\n          {t(\"labels.skip\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default RegisterPasskeyPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/RegistrationInitPage.tsx",
    "content": "import { useContext, useMemo, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport {\n  HankoError,\n  State,\n  generateCodeVerifier,\n  setStoredCodeVerifier, clearStoredCodeVerifier\n} from \"@teamhanko/hanko-frontend-sdk\";\n\nimport { AppContext } from \"../contexts/AppProvider\";\nimport { useFlowState } from \"../hooks/UseFlowState\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Button from \"../components/form/Button\";\nimport Footer from \"../components/wrapper/Footer\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Link from \"../components/link/Link\";\nimport Input from \"../components/form/Input\";\nimport Divider from \"../components/spacer/Divider\";\nimport Checkbox from \"../components/form/Checkbox\";\nimport Spacer from \"../components/spacer/Spacer\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\n\ninterface Props {\n  state: State<\"registration_init\">;\n}\n\nconst RegistrationInitPage = (props: Props) => {\n  const { t } = useContext(TranslateContext);\n  const { init, uiState, setUIState, initialComponentName } =\n    useContext(AppContext);\n  const { flowState } = useFlowState(props.state);\n  const inputs = flowState.actions.register_login_identifier.inputs;\n  const multipleInputsAvailable = !!(inputs?.email && inputs?.username);\n  const [thirdPartyError, setThirdPartyError] = useState<\n    HankoError | undefined\n  >(undefined);\n  const [selectedThirdPartyProvider, setSelectedThirdPartyProvider] = useState<\n    string | null\n  >(null);\n  const [rememberMe, setRememberMe] = useState<boolean>(false);\n  const [isFlowSwitchLoading, setIsFlowSwitchLoading] =\n    useState<boolean>(false);\n\n  const onIdentifierSubmit = async (event: Event) => {\n    event.preventDefault();\n    return await flowState.actions.register_login_identifier.run({\n      email: uiState.email,\n      username: uiState.username,\n    });\n  };\n\n  const onUsernameInput = (event: Event) => {\n    event.preventDefault();\n    if (event.target instanceof HTMLInputElement) {\n      const { value } = event.target;\n      setUIState((prev) => ({ ...prev, username: value }));\n    }\n  };\n\n  const onEmailInput = (event: Event) => {\n    event.preventDefault();\n    if (event.target instanceof HTMLInputElement) {\n      const { value } = event.target;\n      setUIState((prev) => ({ ...prev, email: value }));\n    }\n  };\n\n  const onLoginClick = async (event: Event) => {\n    event.preventDefault();\n    setIsFlowSwitchLoading(true);\n    init(\"login\");\n  };\n\n  const onThirdpartySubmit = async (event: Event, name: string) => {\n    event.preventDefault();\n    setSelectedThirdPartyProvider(name);\n\n    const codeVerifier = generateCodeVerifier();\n    setStoredCodeVerifier(codeVerifier);\n\n    try {\n      const nextState = await flowState.actions.thirdparty_oauth.run(\n        {\n          provider: name,\n          redirect_to: window.location.toString(),\n          code_verifier: codeVerifier,\n        },\n        { dispatchAfterStateChangeEvent: false },\n      );\n\n      if (nextState.error) {\n        clearStoredCodeVerifier();\n        setSelectedThirdPartyProvider(null);\n      }\n\n      nextState.dispatchAfterStateChangeEvent();\n    } catch (e) {\n      clearStoredCodeVerifier();\n      setSelectedThirdPartyProvider(null);\n      throw e;\n    }\n  };\n\n  const onRememberMeChange = async (event: Event) => {\n    event.preventDefault();\n    const nextState = await flowState.actions.remember_me.run(\n      { remember_me: !rememberMe },\n      { dispatchAfterStateChangeEvent: false },\n    );\n    setRememberMe((prev) => !prev);\n    nextState.dispatchAfterStateChangeEvent();\n  };\n\n  const showDivider = useMemo(\n    () =>\n      !!flowState.actions.thirdparty_oauth.enabled &&\n      flowState.actions.register_login_identifier.enabled,\n    [flowState.actions],\n  );\n\n  return (\n    <>\n      <Content>\n        <Headline1>{t(\"headlines.signUp\")}</Headline1>\n        <ErrorBox state={flowState} error={thirdPartyError} />\n        {inputs ? (\n          <>\n            <Form\n              flowAction={flowState.actions.register_login_identifier}\n              onSubmit={onIdentifierSubmit}\n              maxWidth\n            >\n              {inputs.username ? (\n                <Input\n                  markOptional={multipleInputsAvailable}\n                  markError={multipleInputsAvailable}\n                  type={\"text\"}\n                  autoComplete={\"username\"}\n                  autoCorrect={\"off\"}\n                  flowInput={inputs.username}\n                  onInput={onUsernameInput}\n                  value={uiState.username}\n                  placeholder={t(\"labels.username\")}\n                />\n              ) : null}\n              {inputs.email ? (\n                <Input\n                  markOptional={multipleInputsAvailable}\n                  markError={multipleInputsAvailable}\n                  type={\"email\"}\n                  autoComplete={\"email\"}\n                  autoCorrect={\"off\"}\n                  flowInput={inputs.email}\n                  onInput={onEmailInput}\n                  value={uiState.email}\n                  placeholder={t(\"labels.email\")}\n                  pattern={\"^.*[^0-9]+$\"}\n                />\n              ) : null}\n              <Button autofocus>{t(\"labels.continue\")}</Button>\n            </Form>\n            <Divider hidden={!showDivider}>{t(\"labels.or\")}</Divider>\n          </>\n        ) : null}\n        {flowState.actions.thirdparty_oauth.enabled\n          ? flowState.actions.thirdparty_oauth.inputs.provider.allowed_values?.map(\n              (v) => {\n                return (\n                  <Form\n                    flowAction={flowState.actions.thirdparty_oauth}\n                    key={v.value}\n                    onSubmit={(event) => onThirdpartySubmit(event, v.value)}\n                  >\n                    <Button\n                      isLoading={v.value == selectedThirdPartyProvider}\n                      secondary\n                      // @ts-ignore\n                      icon={\n                        v.value.startsWith(\"custom_\")\n                          ? \"customProvider\"\n                          : v.value\n                      }\n                    >\n                      {t(\"labels.signInWith\", { provider: v.name })}\n                    </Button>\n                  </Form>\n                );\n              },\n            )\n          : null}\n        {flowState.actions.remember_me.enabled && (\n          <>\n            <Spacer />\n            <Checkbox\n              required={false}\n              type={\"checkbox\"}\n              label={t(\"labels.staySignedIn\")}\n              checked={rememberMe}\n              onChange={onRememberMeChange}\n            />\n          </>\n        )}\n      </Content>\n      <Footer hidden={initialComponentName !== \"auth\"}>\n        <Paragraph center>\n          <span>{t(\"labels.alreadyHaveAnAccount\")}</span>\n          <Link\n            onClick={onLoginClick}\n            loadingSpinnerPosition={\"left\"}\n            isLoading={isFlowSwitchLoading}\n          >\n            {t(\"labels.signIn\")}\n          </Link>\n        </Paragraph>\n      </Footer>\n    </>\n  );\n};\n\nexport default RegistrationInitPage;\n"
  },
  {
    "path": "frontend/elements/src/pages/RenameWebauthnCredentialPage.tsx",
    "content": "import { useContext, useState } from \"preact/compat\";\nimport { TranslateContext } from \"@denysvuika/preact-translate\";\nimport { State, WebauthnCredential } from \"@teamhanko/hanko-frontend-sdk\";\n\nimport Content from \"../components/wrapper/Content\";\nimport Form from \"../components/form/Form\";\nimport Input from \"../components/form/Input\";\nimport Button from \"../components/form/Button\";\nimport ErrorBox from \"../components/error/ErrorBox\";\nimport Paragraph from \"../components/paragraph/Paragraph\";\nimport Headline1 from \"../components/headline/Headline1\";\nimport Footer from \"../components/wrapper/Footer\";\nimport Link from \"../components/link/Link\";\n\ntype Props = {\n  oldName: string;\n  credential: WebauthnCredential;\n  credentialType: \"passkey\" | \"security-key\";\n  onBack: (event: Event) => Promise<void>;\n  onCredentialNameSubmit: (\n    event: Event,\n    id: string,\n    name: string,\n  ) => Promise<void>;\n  flowState: State<\"profile_init\">;\n};\n\nconst RenameWebauthnCredentialPage = ({\n  onCredentialNameSubmit,\n  oldName,\n  onBack,\n  credential,\n  credentialType,\n  flowState,\n}: Props) => {\n  const { t } = useContext(TranslateContext);\n  const [newName, setNewName] = useState<string>(oldName);\n\n  const onInput = async (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      setNewName(event.target.value);\n    }\n  };\n\n  return (\n    <>\n      <Content>\n        <Headline1>\n          {credentialType === \"security-key\"\n            ? t(\"headlines.renameSecurityKey\")\n            : t(\"headlines.renamePasskey\")}\n        </Headline1>\n        <ErrorBox flowError={null} />\n        <Paragraph>\n          {credentialType === \"security-key\"\n            ? t(\"texts.renameSecurityKey\")\n            : t(\"texts.renamePasskey\")}\n        </Paragraph>\n        <Form\n          flowAction={flowState.actions.webauthn_credential_rename}\n          onSubmit={(event: Event) =>\n            onCredentialNameSubmit(event, credential.id, newName)\n          }\n        >\n          <Input\n            type={\"text\"}\n            name={credentialType}\n            value={newName}\n            minLength={3}\n            maxLength={32}\n            required={true}\n            placeholder={\n              credentialType === \"security-key\"\n                ? t(\"labels.newSecurityKeyName\")\n                : t(\"labels.newPasskeyName\")\n            }\n            onInput={onInput}\n            autofocus\n          />\n          <Button>{t(\"labels.save\")}</Button>\n        </Form>\n      </Content>\n      <Footer>\n        <Link onClick={onBack} loadingSpinnerPosition={\"right\"}>\n          {t(\"labels.back\")}\n        </Link>\n      </Footer>\n    </>\n  );\n};\n\nexport default RenameWebauthnCredentialPage;\n"
  },
  {
    "path": "frontend/elements/tsconfig.json",
    "content": "{\n  \"include\": [\n    \"src/**/*\"\n  ],\n  \"compilerOptions\": {\n    \"paths\": {\n      \"*\": [\n        \"./*\"\n      ]\n    },\n    \"esModuleInterop\": true,\n    \"baseUrl\": \"./src\",\n    \"moduleResolution\": \"node\",\n    \"allowSyntheticDefaultImports\": true,\n    \"isolatedModules\": true,\n    \"resolveJsonModule\": true,\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"preact\",\n    \"noResolve\": false,\n    \"declaration\": true,\n    \"diagnostics\": true,\n    \"outDir\": \"./dist\",\n    \"lib\":[\"ES2015\", \"ES2016\", \"dom\"],\n    \"sourceMap\": true,\n    \"noImplicitAny\": true,\n    \"target\": \"es6\",\n    \"module\": \"es2020\"\n  }\n}\n"
  },
  {
    "path": "frontend/elements/webpack.config.cjs",
    "content": "const path = require(\"path\");\n\nmodule.exports = {\n  experiments: {\n    outputModule: true,\n  },\n  entry: {\n    hankoElements: {\n      filename: \"elements.js\",\n      import: \"./src/index.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    de: {\n      filename: \"i18n/de.js\",\n      import: \"./src/i18n/de.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    en: {\n      filename: \"i18n/en.js\",\n      import: \"./src/i18n/en.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    fr: {\n      filename: \"i18n/fr.js\",\n      import: \"./src/i18n/fr.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    it: {\n      filename: \"i18n/it.js\",\n      import: \"./src/i18n/it.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    nl: {\n      filename: \"i18n/nl.js\",\n      import: \"./src/i18n/nl.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    ptBR: {\n      filename: \"i18n/pt-BR.js\",\n      import: \"./src/i18n/pt-BR.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    zr: {\n      filename: \"i18n/zh.js\",\n      import: \"./src/i18n/zh.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    bn: {\n      filename: \"i18n/bn.js\",\n      import: \"./src/i18n/bn.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n    all: {\n      filename: \"i18n/all.js\",\n      import: \"./src/i18n/all.ts\",\n      library: {\n        type: \"module\",\n      },\n    },\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.(tsx?)$/,\n        use: \"ts-loader\",\n        exclude: [/node_modules/, /dist/],\n        resolve: {\n          fullySpecified: false,\n        },\n      },\n      {\n        test: /\\.(sass)$/,\n        use: [\n          {\n            loader: \"style-loader\",\n            options: {\n              injectType: \"singletonStyleTag\",\n              insert: (styleTag) => {\n                // eslint-disable-next-line no-underscore-dangle\n                window._hankoStyle = styleTag;\n              },\n            },\n          },\n          {\n            loader: \"css-loader\",\n            options: {\n              modules: {\n                localIdentName: \"hanko_[local]\",\n                localIdentContext: path.resolve(__dirname, \"src\"),\n                namedExport: false,\n              },\n              importLoaders: 1,\n            },\n          },\n          {\n            loader: \"sass-loader\",\n            options: {\n              sourceMap: true,\n            },\n          },\n        ],\n      },\n    ],\n  },\n  resolve: {\n    extensions: [\".ts\", \".tsx\", \".js\", \".sass\", \"declarations.d.ts\"],\n  },\n  output: {\n    clean: true,\n    path: path.resolve(__dirname, \"dist\"),\n  },\n};\n"
  },
  {
    "path": "frontend/elements/webpack.config.dev.cjs",
    "content": "const baseConfig = require(\"./webpack.config.cjs\");\n\nbaseConfig.module.rules.push({\n  test: /\\.c?js$/,\n  enforce: \"pre\",\n  use: [\"source-map-loader\"],\n})\n\nmodule.exports = {\n  devtool: 'eval-source-map',\n  ...baseConfig\n};\n"
  },
  {
    "path": "frontend/examples/README.md",
    "content": "# Hanko Examples\n\nThis directory contains examples that show\n\n- integration of web component(s) provided through the`@teamhanko/hanko-elements` package (see [elements](../elements)).\n- how to validate JSON Web Tokens (JWT) issued by the Hanko [API](../../backend) in a custom backend\n\nIt contains:\n\n- an example [express](express) backend - this is a simple version of the well-known todo app\n- example frontend applications using the following frameworks:\n  - [Angular](angular)\n  - [Next.js](nextjs)\n  - [React](react)\n  - [Vue](vue)\n  - [Svelte](svelte)\n\n## How to run\n### Manual\n1. Start the Hanko API (see the instructions on how to run the API [in Docker](../../backend/README.md#Docker) or [from Source](../../backend/README.md#from-source))\n2. Start the express backend (see the [README](express) for the express backend)\n3. Start one of the frontend applications (see the README for the app of your choice)\n\n### Docker Compose\n\n#### React\n```\ndocker compose -f deploy/docker-compose/base.yaml -f deploy/docker-compose/todo-react.yaml -p \"hanko-todo-react\" up --build\n```\n#### Angular\n```\ndocker compose -f deploy/docker-compose/base.yaml -f deploy/docker-compose/todo-angular.yaml -p \"hanko-todo-angular\" up --build\n```\n#### Next.js\n```\ndocker compose -f deploy/docker-compose/base.yaml -f deploy/docker-compose/todo-nextjs.yaml -p \"hanko-todo-nextjs\" up --build\n```\n#### Vue\n```\ndocker compose -f deploy/docker-compose/base.yaml -f deploy/docker-compose/todo-vue.yaml -p \"hanko-todo-vue\" up --build\n```\n#### Svelte\n```\ndocker compose -f deploy/docker-compose/base.yaml -f deploy/docker-compose/todo-svelte.yaml -p \"hanko-todo-svelte\" up --build\n```\n"
  },
  {
    "path": "frontend/examples/angular/.browserslistrc",
    "content": "# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.\n# For additional information regarding the format and rule options, please see:\n# https://github.com/browserslist/browserslist#queries\n\n# For the full list of supported browsers by the Angular framework, please see:\n# https://angular.io/guide/browser-support\n\n# You can see what browsers were selected by your queries by running:\n#   npx browserslist\n\nlast 1 Chrome version\nlast 1 Firefox version\nlast 2 Edge major versions\nlast 2 Safari major versions\nlast 2 iOS major versions\nFirefox ESR\n"
  },
  {
    "path": "frontend/examples/angular/.dockerignore",
    "content": "node_modules\n"
  },
  {
    "path": "frontend/examples/angular/.editorconfig",
    "content": "# Editor configuration, see https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.ts]\nquote_type = single\n\n[*.md]\nmax_line_length = off\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "frontend/examples/angular/.gitignore",
    "content": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "frontend/examples/angular/Dockerfile",
    "content": "# pull official base image\nFROM node:16-alpine\n\n# set working directory\nWORKDIR /app\n\n# add `/app/node_modules/.bin` to $PATH\nENV PATH /app/node_modules/.bin:$PATH\n\n# install app dependencies\nCOPY package.json ./\nRUN npm install\n\n# add app\nCOPY . ./\n\n# start app\nCMD [\"npm\", \"run\", \"start-docker\"]\n"
  },
  {
    "path": "frontend/examples/angular/README.md",
    "content": "# Hanko Angular example\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.3.\n\n## Starting the app\n### Prerequisites\n\n- a running Hanko API (see the instructions on how to run the API [in Docker](../../../backend/README.md#Docker) or [from Source](../../../backend/README.md#from-source))\n- a running express backend (see the [README](../express) for the express backend)\n\n### Set up environment variables\n\nIn the `src/environments/environment.ts` file set up the correct environment variables:\n\n- `hankoApi`: this is the URL of the Hanko API (default: `http://localhost:8000`, can be customized using the \n  `server.public.address` option in the [backend configuration](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-public#address))\n- `todoApi`: this is the URL of the [express](../express) backend (default: `http://localhost:8002`)\n\n### Run development server\n\nRun `npm install` to install dependencies, then run `npm run start` for a development server. Navigate to `http://localhost:8888/`. The application will automatically reload if you change any of the source files.\n"
  },
  {
    "path": "frontend/examples/angular/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"projects\": {\n    \"angular\": {\n      \"projectType\": \"application\",\n      \"schematics\": {},\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:browser\",\n          \"options\": {\n            \"outputPath\": \"dist/angular\",\n            \"index\": \"src/index.html\",\n            \"main\": \"src/main.ts\",\n            \"polyfills\": \"src/polyfills.ts\",\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\n              \"src/favicon.png\",\n              \"src/assets\"\n            ],\n            \"styles\": [\n              \"src/styles.css\"\n            ],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"budgets\": [\n                {\n                  \"type\": \"initial\",\n                  \"maximumWarning\": \"500kb\",\n                  \"maximumError\": \"1mb\"\n                },\n                {\n                  \"type\": \"anyComponentStyle\",\n                  \"maximumWarning\": \"2kb\",\n                  \"maximumError\": \"4kb\"\n                }\n              ],\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"buildOptimizer\": false,\n              \"optimization\": false,\n              \"vendorChunk\": true,\n              \"extractLicenses\": false,\n              \"sourceMap\": true,\n              \"namedChunks\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"angular:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"angular:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"angular:build\"\n          }\n        },\n        \"test\": {\n          \"builder\": \"@angular-devkit/build-angular:karma\",\n          \"options\": {\n            \"main\": \"src/test.ts\",\n            \"polyfills\": \"src/polyfills.ts\",\n            \"tsConfig\": \"tsconfig.spec.json\",\n            \"karmaConfig\": \"karma.conf.js\",\n            \"assets\": [\n              \"src/favicon.ico\",\n              \"src/assets\"\n            ],\n            \"styles\": [\n              \"src/styles.css\"\n            ],\n            \"scripts\": []\n          }\n        }\n      }\n    }\n  },\n  \"cli\": {\n    \"analytics\": false\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/package.json",
    "content": "{\n  \"name\": \"example-angular\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"ng serve --port 8888\",\n    \"start-docker\": \"ng serve --host=0.0.0.0 --port 8888\",\n    \"build\": \"ng build\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^21.1.5\",\n    \"@angular/common\": \"^21.1.5\",\n    \"@angular/compiler\": \"^21.1.5\",\n    \"@angular/core\": \"^21.1.5\",\n    \"@angular/forms\": \"^21.1.5\",\n    \"@angular/platform-browser\": \"^21.1.5\",\n    \"@angular/platform-browser-dynamic\": \"^21.1.5\",\n    \"@angular/router\": \"^21.1.5\",\n    \"rxjs\": \"~7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"~0.16.0\",\n    \"@teamhanko/hanko-elements\": \"^2.5.0\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^21.1.5\",\n    \"@angular/cli\": \"^21.1.5\",\n    \"@angular/compiler-cli\": \"^21.1.5\",\n    \"@types/node\": \"^22.5.1\",\n    \"copyfiles\": \"^2.4.1\",\n    \"ts-node\": \"~10.9.2\",\n    \"typescript\": \"~5.9.3\"\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/app-routing.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\nimport { LoginComponent } from './login/login.component';\nimport { TodoComponent } from './todo/todo.component';\nimport { ProfileComponent } from \"./profile/profile.component\";\n\nconst routes: Routes = [\n  {\n    path: 'login',\n    component: LoginComponent,\n    data: { title: 'Login' },\n  },\n  {\n    path: 'todo',\n    component: TodoComponent,\n    data: { title: 'Todo' },\n  },\n  {\n    path: 'profile',\n    component: ProfileComponent,\n    data: { title: 'Profile' },\n  },\n  { path: '', redirectTo: '/login', pathMatch: 'full' },\n  { path: '**', component: LoginComponent },\n];\n\n@NgModule({\n  imports: [RouterModule.forRoot(routes)],\n  exports: [RouterModule],\n})\nexport class AppRoutingModule {}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/app.component.css",
    "content": ".nav {\n  width: 100%;\n  padding: 10px;\n  opacity: 0.9;\n}\n\n.button {\n  font-size: 1rem;\n  border: none;\n  background: none;\n  cursor: pointer;\n}\n\n.button:disabled {\n  color: grey!important;\n  cursor: default;\n  text-decoration: none!important;\n}\n\n.nav .button:hover {\n  text-decoration: underline;\n}\n\n.nav .button {\n  color: white;\n  float: right;\n}\n\n.content {\n  padding: 24px;\n  border-radius: 17px;\n  color: black;\n  background-color: white;\n  width: 100%;\n  max-width: 500px;\n  min-width: 330px;\n  margin: 10vh auto;\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/app.component.html",
    "content": "<router-outlet></router-outlet>\n"
  },
  {
    "path": "frontend/examples/angular/src/app/app.component.ts",
    "content": "import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n    selector: 'app-root',\n    templateUrl: './app.component.html',\n    styleUrls: ['./app.component.css'],\n    standalone: true,\n    imports: [RouterOutlet]\n})\nexport class AppComponent {\n  title = 'angular';\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/login/login.component.html",
    "content": "<div class=\"content\">\n  <div class=\"error\">{{ error?.message }}</div>\n  <hanko-auth (onSessionCreated)=\"redirectToTodos()\"></hanko-auth>\n</div>\n"
  },
  {
    "path": "frontend/examples/angular/src/app/login/login.component.ts",
    "content": "import { Component, CUSTOM_ELEMENTS_SCHEMA } from \"@angular/core\";\nimport { HankoService } from \"../services/hanko.services\";\nimport { Router } from \"@angular/router\";\n\n@Component({\n    selector: 'app-login',\n    templateUrl: './login.component.html',\n    styleUrls: ['../app.component.css'],\n    standalone: true,\n    schemas: [CUSTOM_ELEMENTS_SCHEMA]\n})\nexport class LoginComponent {\n  error?: Error;\n\n  constructor(private hankoService: HankoService, private router: Router) {}\n\n  redirectToTodos() {\n    this.router.navigate(['/todo']).catch((e) => (this.error = e));\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/modal/session-expired-modal.component.html",
    "content": "<hanko-events (onSessionExpired)=\"show()\"></hanko-events>\n<dialog #modal id=\"session-expired-modal\">\n  <div class=\"error\">{{ error?.message }}</div>\n  Please login again.<br><br>\n  <button (click)=\"redirectToLogin()\">Login</button>\n</dialog>\n"
  },
  {
    "path": "frontend/examples/angular/src/app/modal/session-expired-modal.component.ts",
    "content": "import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from \"@angular/core\";\nimport { Router } from '@angular/router';\nimport { HankoService } from \"../services/hanko.services\";\n\n@Component({\n    selector: 'app-session-expired-modal',\n    templateUrl: './session-expired-modal.component.html',\n    styleUrls: ['../app.component.css'],\n    standalone: true,\n    schemas: [CUSTOM_ELEMENTS_SCHEMA]\n})\nexport class SessionExpiredModalComponent {\n  @ViewChild('modal') modal?: ElementRef<HTMLDialogElement>;\n  error?: Error;\n\n  constructor(private hankoService: HankoService, private router: Router) {}\n\n  redirectToLogin() {\n    this.router.navigate(['/']).catch((e) => (this.error = e));\n  }\n\n  show() {\n    this.modal?.nativeElement.showModal();\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/profile/profile.component.html",
    "content": "<app-session-expired-modal></app-session-expired-modal>\n<nav class=\"nav\">\n  <button (click)=\"logout()\" class=\"button\">Logout</button>\n  <button disabled class=\"button\">Profile</button>\n  <button (click)=\"redirectToTodos()\" class=\"button\">Todos</button>\n</nav>\n<div class=\"content\">\n  <div class=\"error\">{{ error?.message }}</div>\n  <hanko-profile (onUserLoggedOut)=\"redirectToLogin()\"></hanko-profile>\n</div>\n"
  },
  {
    "path": "frontend/examples/angular/src/app/profile/profile.component.ts",
    "content": "import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit } from \"@angular/core\";\nimport { Router } from '@angular/router';\nimport { TodoService } from '../services/todo.service';\nimport { HankoService } from \"../services/hanko.services\";\nimport { SessionExpiredModalComponent } from '../modal/session-expired-modal.component';\n\n@Component({\n    selector: 'app-profile',\n    templateUrl: './profile.component.html',\n    styleUrls: ['../app.component.css'],\n    standalone: true,\n    imports: [SessionExpiredModalComponent],\n    schemas: [CUSTOM_ELEMENTS_SCHEMA]\n})\nexport class ProfileComponent implements OnInit {\n  error?: Error;\n\n  constructor(private hankoService: HankoService, private todoService: TodoService, private router: Router) {}\n\n  async ngOnInit() {\n    const { is_valid} = await this.hankoService.client.validateSession();\n    if (!is_valid) {\n      this.redirectToLogin();\n    }\n  }\n\n  logout() {\n    this.hankoService.client.logout().catch((e) => (this.error = e));\n  }\n\n  redirectToLogin() {\n    this.router.navigate(['/']).catch((e) => (this.error = e));\n  }\n\n  redirectToTodos() {\n    this.router.navigate(['/todo']).catch((e) => (this.error = e));\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/services/hanko.services.ts",
    "content": "import { Injectable } from \"@angular/core\";\nimport { environment } from \"../../environments/environment\";\nimport { Hanko, register } from \"@teamhanko/hanko-elements\";\n\n@Injectable({\n  providedIn: 'root',\n})\nexport class HankoService {\n  api = environment.hankoApi\n  client = new Hanko(this.api)\n\n  constructor() {\n    register(this.api).catch(console.error);\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/services/todo.service.ts",
    "content": "import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\n\nexport interface Todo {\n  todoID?: string;\n  description: string;\n  checked: boolean;\n}\n\nexport type Todos = Todo[];\n\n@Injectable({\n  providedIn: 'root',\n})\nexport class TodoService {\n  api = 'http://localhost:8002';\n\n  constructor() {\n    this.api = environment.todoApi;\n  }\n\n  addTodo(todo: Todo) {\n    return fetch(`${this.api}/todo`, {\n      method: 'POST',\n      credentials: 'include',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify(todo),\n    });\n  }\n\n  listTodos() {\n    return fetch(`${this.api}/todo`, {\n      credentials: 'include',\n    });\n  }\n\n  patchTodo(id: string, checked: boolean) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: 'PATCH',\n      credentials: 'include',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({checked}),\n    });\n  }\n\n  deleteTodo(id: string) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: 'DELETE',\n      credentials: 'include',\n    });\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/todo/todo.component.css",
    "content": ".headline {\n  text-align: center;\n  margin-top: 0;\n}\n\n.form {\n  display: flex;\n  margin-bottom: 17px;\n}\n\n.form .input {\n  flex-grow: 1;\n  margin-right: 10px;\n}\n\n.form .button {\n  color: black;\n}\n\n.list {\n  display: flex;\n  flex-direction: column;\n  row-gap: 7px;\n}\n\n.item {\n  display: flex;\n  justify-content: space-between;\n  align-items: flex-start;\n  column-gap: 7px;\n}\n\n.description {\n  flex-grow: 1;\n  cursor: pointer;\n}\n\n.error {\n  color: red;\n  padding: 0 0 10px;\n}\n\n.input {\n  border: 1px solid black;\n  border-radius: 2.4px;\n}\n\n.checkbox {\n  margin-left: 0;\n  -webkit-appearance: none;\n  appearance: none;\n  background-color: #fff;\n  font: inherit;\n  color: currentColor;\n  width: 1em;\n  height: 1em;\n  border: 1px solid currentColor;\n  border-radius: 0.15em;\n  transform: translateY(-0.075em);\n  display: grid;\n  place-content: center;\n}\n\n.checkbox::before {\n  content: \"\";\n  width: 0.65em;\n  height: 0.65em;\n  transform: scale(0);\n  transition: 120ms transform ease-in-out;\n  box-shadow: inset 1em 1em black;\n\n  transform-origin: bottom left;\n  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);\n}\n\n.checkbox:checked::before {\n  transform: scale(1);\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/app/todo/todo.component.html",
    "content": "<app-session-expired-modal></app-session-expired-modal>\n<hanko-events (onUserLoggedOut)=\"redirectToLogin()\"></hanko-events>\n<nav class=\"nav\">\n  <button (click)=\"logout()\" class=\"button\">Logout</button>\n  <button (click)=\"redirectToProfile()\" class=\"button\">Profile</button>\n  <button disabled class=\"button\">Todos</button>\n</nav>\n<div class=\"content\">\n  <h1 class=\"headline\">Todos</h1>\n  <div class=\"error\">{{ error?.message }}</div>\n  <form (submit)=\"addTodo($event)\" class=\"form\">\n    <input\n      required\n      class=\"input\"\n      type=\"text\"\n      value=\"{{ description }}\"\n      (change)=\"changeDescription($event)\"\n    />\n    <button type=\"submit\" class=\"button\">+</button>\n  </form>\n  <div class=\"list\">\n    @for (todo of todos; track todo.todoID) {\n      <div class=\"item\">\n        <input\n          class=\"checkbox\"\n          [id]=\"todo.todoID\"\n          type=\"checkbox\"\n          [value]=\"todo.todoID\"\n          [checked]=\"todo.checked\"\n          (change)=\"changeCheckbox($event)\"\n        />\n\n        <label class=\"description\" [for]=\"todo.todoID\">\n          {{ todo.description }}\n        </label>\n\n        <button\n          class=\"button\"\n          (click)=\"deleteTodo(todo.todoID!)\"\n        >\n          ×\n        </button>\n      </div>\n    } @empty {\n      <div class=\"item empty\">\n        No todos available\n      </div>\n    }\n  </div>\n</div>\n"
  },
  {
    "path": "frontend/examples/angular/src/app/todo/todo.component.ts",
    "content": "import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, ViewChild } from \"@angular/core\";\nimport { Router } from '@angular/router';\nimport { Todos, TodoService } from '../services/todo.service';\nimport { HankoService } from \"../services/hanko.services\";\nimport { SessionExpiredModalComponent } from \"../modal/session-expired-modal.component\";\n\n@Component({\n    selector: 'app-todo',\n    templateUrl: './todo.component.html',\n    styleUrls: ['../app.component.css', './todo.component.css'],\n    standalone: true,\n    imports: [SessionExpiredModalComponent],\n    schemas: [CUSTOM_ELEMENTS_SCHEMA]\n})\nexport class TodoComponent implements OnInit {\n  todos: Todos = [];\n  error?: Error;\n  description = '';\n\n  constructor(private hankoService: HankoService, private todoService: TodoService, private router: Router) {}\n\n  @ViewChild(SessionExpiredModalComponent)\n  private sessionExpiredModalComponent!: SessionExpiredModalComponent;\n\n  async ngOnInit() {\n    const { is_valid} = await this.hankoService.client.validateSession();\n    if (is_valid) {\n        this.listTodos();\n      } else {\n        this.redirectToLogin();\n      }\n  }\n\n  changeDescription(event: any) {\n    this.description = event.target.value;\n  }\n\n  changeCheckbox(event: any) {\n    const { currentTarget } = event;\n    this.patchTodo(currentTarget.value, currentTarget.checked);\n  }\n\n  addTodo(event: any) {\n    event.preventDefault();\n    const entry = { description: this.description, checked: false };\n\n    this.todoService\n      .addTodo(entry)\n      .then((res) => {\n        if (res.status === 401) {\n          this.sessionExpiredModalComponent.show();\n          return;\n        }\n\n        this.description = '';\n        this.listTodos();\n\n        return;\n      })\n      .catch((e) => (this.error = e));\n  }\n\n  patchTodo(id: string, checked: boolean) {\n    this.todoService\n      .patchTodo(id, checked)\n      .then((res) => {\n        if (res.status === 401) {\n          this.sessionExpiredModalComponent.show();\n          return;\n        }\n\n        this.listTodos();\n\n        return;\n      })\n      .catch((e) => (this.error = e));\n  }\n\n  listTodos() {\n    this.todoService\n      .listTodos()\n      .then((res) => {\n        if (res.status === 401) {\n          this.sessionExpiredModalComponent.show();\n          return;\n        }\n\n        return res.json();\n      })\n      .then((todo) => {\n        if (todo) {\n          this.todos = todo;\n        }\n      })\n      .catch((e) => (this.error = e));\n  }\n\n  deleteTodo(id: string) {\n    this.todoService\n      .deleteTodo(id)\n      .then((res) => {\n        if (res.status === 401) {\n          this.sessionExpiredModalComponent.show();\n          return;\n        }\n\n        this.listTodos();\n\n        return;\n      })\n      .catch((e) => (this.error = e));\n  }\n\n  logout() {\n    this.hankoService.client.logout().catch((e) => (this.error = e));\n  }\n\n  redirectToLogin() {\n    this.router.navigate(['/']).catch((e) => (this.error = e));\n  }\n\n  redirectToProfile() {\n    this.router.navigate(['/profile']).catch((e) => (this.error = e));\n  }\n}\n"
  },
  {
    "path": "frontend/examples/angular/src/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "frontend/examples/angular/src/environments/environment.ts",
    "content": "// This file can be replaced during build by using the `fileReplacements` array.\n// `ng build` replaces `environment.ts` with `environment.prod.ts`.\n// The list of file replacements can be found in `angular.json`.\n\nexport const environment = {\n  production: false,\n  hankoApi: 'http://localhost:8000',\n  todoApi: 'http://localhost:8002',\n};\n\n/*\n * For easier debugging in development mode, you can import the following file\n * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.\n *\n * This import should be commented out in production mode because it will have a negative impact\n * on performance if an error is thrown.\n */\n// import 'zone.js/plugins/zone-error';  // Included with Angular CLI.\n"
  },
  {
    "path": "frontend/examples/angular/src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Angular</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" href=\"favicon.png\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "frontend/examples/angular/src/main.ts",
    "content": "import { enableProdMode, importProvidersFrom } from '@angular/core';\nimport { environment } from './environments/environment';\nimport { AppComponent } from './app/app.component';\nimport { AppRoutingModule } from './app/app-routing.module';\nimport { BrowserModule, bootstrapApplication } from '@angular/platform-browser';\n\nif (environment.production) {\n  enableProdMode();\n}\n\nbootstrapApplication(AppComponent, {\n    providers: [importProvidersFrom(BrowserModule, AppRoutingModule)]\n})\n  .catch((err) => console.error(err));\n"
  },
  {
    "path": "frontend/examples/angular/src/polyfills.ts",
    "content": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfills to this file.\n *\n * This file is divided into 2 sections:\n *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.\n *   2. Application imports. Files imported after ZoneJS that should be loaded before your main\n *      file.\n *\n * The current setup is for so-called \"evergreen\" browsers; the last versions of browsers that\n * automatically update themselves. This includes recent versions of Safari, Chrome (including\n * Opera), Edge on the desktop, and iOS and Chrome on mobile.\n *\n * Learn more in https://angular.io/guide/browser-support\n */\n\n/***************************************************************************************************\n * BROWSER POLYFILLS\n */\n\n/**\n * By default, zone.js will patch all possible macroTask and DomEvents\n * user can disable parts of macroTask/DomEvents patch by setting following flags\n * because those flags need to be set before `zone.js` being loaded, and webpack\n * will put import in the top of bundle, so user need to create a separate file\n * in this directory (for example: zone-flags.ts), and put the following flags\n * into that file, and then add the following code before importing zone.js.\n * import './zone-flags';\n *\n * The flags allowed in zone-flags.ts are listed here.\n *\n * The following flags will work for all browsers.\n *\n * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame\n * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick\n * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames\n *\n *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js\n *  with the following flag, it will bypass `zone.js` patch for IE/Edge\n *\n *  (window as any).__Zone_enable_cross_context_check = true;\n *\n */\n\n/***************************************************************************************************\n * Zone JS is required by default for Angular itself.\n */\nimport 'zone.js'; // Included with Angular CLI.\n\n/***************************************************************************************************\n * APPLICATION IMPORTS\n */\n"
  },
  {
    "path": "frontend/examples/angular/src/styles.css",
    "content": "/* You can add global styles to this file, and also import other style files */\nbody {\n  color: white;\n  margin: 0;\n  background: url(\"assets/bg.jpg\") no-repeat center center fixed;\n  background-size: cover;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n    \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n    sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhanko-auth::part(form-item) {\n  min-width: 100%; /* input fields and buttons are on top of each other  */\n}\n\ndialog {\n  overflow: hidden;\n  border-radius: 5px;\n  box-shadow: 0 0 30px 7px #111;\n}\n"
  },
  {
    "path": "frontend/examples/angular/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\n    \"src/main.ts\",\n    \"src/polyfills.ts\"\n  ],\n  \"include\": [\n    \"src/**/*.d.ts\"\n  ]\n}\n"
  },
  {
    "path": "frontend/examples/angular/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"outDir\": \"./dist/out-tsc\",\n    \"forceConsistentCasingInFileNames\": true,\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"downlevelIteration\": true,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"],\n    \"skipLibCheck\": true,\n    \"paths\": {\n      \"@angular/*\": [\"../../node_modules/@angular/*\"]\n    }\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "frontend/examples/express/.dockerignore",
    "content": "node_modules\n"
  },
  {
    "path": "frontend/examples/express/.gitignore",
    "content": "**/node_modules\r\n"
  },
  {
    "path": "frontend/examples/express/Dockerfile",
    "content": "# pull official base image\nFROM node:16-alpine\n\n# set working directory\nWORKDIR /app\n\n# add `/app/node_modules/.bin` to $PATH\nENV PATH /app/node_modules/.bin:$PATH\n\n# install app dependencies\nCOPY package.json ./\nRUN npm install --silent\nRUN npm install react-scripts@3.4.1 -g --silent\n\n# add app\nCOPY . ./\n\n# start app\nCMD [\"npm\", \"start\"]\n"
  },
  {
    "path": "frontend/examples/express/README.md",
    "content": "# Hanko Express Example\n\nThis directory contains an [express](https://expressjs.com) application that serves as the backend for the example\nfrontend applications contained in the [examples](../../examples) directory. It is a simple API for creating, listing and\nupdating \"todos\".\n\n## Starting the app\n\n### Set up environment variables\n\nIn the `.env` file set up the correct environment variables:\n\n- `HANKO_API_URL`: this is the URL of the Hanko API (default: `http://localhost:8000`, can be customized using the\n  `server.public.address` option in the [backend configuration](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-public#address))\n\n### Run the server\n\nRun `npm install` to install dependencies, then run `npm run start`. The API will be available on `http://localhost:8002/`.\n"
  },
  {
    "path": "frontend/examples/express/package.json",
    "content": "{\n  \"name\": \"example-express\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"node src/server.js\"\n  },\n  \"dependencies\": {\n    \"cookie-parser\": \"^1.4.6\",\n    \"cors\": \"^2.8.6\",\n    \"dotenv\": \"^17.3.1\",\n    \"express\": \"^5.2.1\",\n    \"express-jwt\": \"^8.3.0\",\n    \"jwks-rsa\": \"^4.0.1\"\n  }\n}\n"
  },
  {
    "path": "frontend/examples/express/src/server.js",
    "content": "const express = require(\"express\");\nconst { expressjwt: jwt } = require(\"express-jwt\");\nconst jwksRsa = require(\"jwks-rsa\");\nconst cors = require(\"cors\");\nconst cookieParser = require(\"cookie-parser\");\nconst crypto = require(\"crypto\");\n\nrequire(\"dotenv\").config();\n\nconst app = express();\nconst jwksHost = process.env.HANKO_API_URL;\n\nconst corsOptions = {\n  origin: \"http://localhost:8888\",\n  credentials: true,\n  methods: [\"GET\", \"POST\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n};\n\nconst store = new Map();\n\napp.use(cors(corsOptions));\napp.use(cookieParser());\napp.use(express.json());\napp.use(\n  jwt({\n    secret: jwksRsa.expressJwtSecret({\n      cache: true,\n      rateLimit: true,\n      jwksRequestsPerMinute: 100,\n      jwksUri: `${jwksHost}/.well-known/jwks.json`,\n    }),\n    algorithms: [\"RS256\"],\n    getToken: function fromCookieOrHeader(req) {\n      if (\n        req.headers.authorization &&\n        req.headers.authorization.split(\" \")[0] === \"Bearer\"\n      ) {\n        return req.headers.authorization.split(\" \")[1];\n      } else if (req.cookies && req.cookies.hanko) {\n        return req.cookies.hanko;\n      }\n      return null;\n    },\n  })\n);\n\napp.get(\"/todo\", (req, res) => {\n  const userID = req.auth.sub;\n  const todos = store.get(userID) || new Map();\n  res.status(200).send(Array.from(todos.values()));\n});\n\napp.post(\"/todo\", (req, res) => {\n  const todoID = crypto.randomBytes(16).toString(\"hex\");\n  const { description, checked } = req.body;\n  const userID = req.auth.sub;\n  const todos = store.get(userID) || new Map();\n\n  todos.set(todoID, { todoID, description, checked });\n  store.set(userID, todos);\n  res.status(201).end();\n});\n\napp.patch(\"/todo/:id\", (req, res) => {\n  const userID = req.auth.sub;\n  const todoID = req.params.id;\n  const todos = store.get(userID);\n\n  if (req.body.hasOwnProperty(\"checked\")) {\n    const checked = req.body.checked;\n\n    if (todos.has(todoID)) {\n      todos.get(todoID).checked = checked;\n    }\n  }\n\n  res.status(204).end();\n});\n\napp.delete(\"/todo/:id\", (req, res) => {\n  const userID = req.auth.sub;\n  const todoID = req.params.id;\n  const todos = store.get(userID);\n\n  todos.delete(todoID);\n  res.status(204).end();\n});\n\napp.listen(8002);\n"
  },
  {
    "path": "frontend/examples/nextjs/.dockerignore",
    "content": "node_modules\n"
  },
  {
    "path": "frontend/examples/nextjs/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "frontend/examples/nextjs/Dockerfile",
    "content": "# pull official base image\nFROM node:18.17-alpine\n\n# set working directory\nWORKDIR /app\n\n# add `/app/node_modules/.bin` to $PATH\nENV PATH /app/node_modules/.bin:$PATH\n\n# install app dependencies\nCOPY package.json ./\nRUN npm install\n\n# add app\nCOPY . ./\n\n# start app\nCMD [\"npm\", \"start\"]\n"
  },
  {
    "path": "frontend/examples/nextjs/README.md",
    "content": "# Hanko Next.js example\n\nThis is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Starting the app\n\n### Prerequisites\n\n- a running Hanko API (see the instructions on how to run the API [in Docker](../../../backend/README.md#Docker) or [from Source](../../../backend/README.md#from-source))\n- a running express backend (see the [README](../express) for the express backend)\n\n### Set up environment variables\n\nIn the `.env` file set up the correct environment variables:\n\n- `NEXT_PUBLIC_HANKO_API`: this is the URL of the Hanko API (default: `http://localhost:8000`, can be customized using\n  the `server.public.address` option in the [backend configuration](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-public#address))\n- `NEXT_PUBLIC_TODO_API`: this is the URL of the [express](../express) backend (default: `http://localhost:8002`)\n\n### Run development server\n\nRun `npm install` to install dependencies, then run `npm run start` for a development server. Navigate to `http://localhost:8888/`. The application will automatically reload if you change any of the source files.\n"
  },
  {
    "path": "frontend/examples/nextjs/components/HankoAuth.tsx",
    "content": "import { Hanko, register } from \"@teamhanko/hanko-elements\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useRouter } from \"next/router\";\n\nconst api = process.env.NEXT_PUBLIC_HANKO_API!;\n\ninterface Props {\n  setError(error: Error): void;\n}\n\nfunction HankoAuth({ setError }: Props) {\n  const router = useRouter();\n  const [hankoClient, setHankoClient] = useState<Hanko>();\n\n  const redirectToTodos = useCallback(() => {\n    router.replace(\"/todo\").catch(setError);\n  }, [router, setError]);\n\n  useEffect(() => setHankoClient(new Hanko(api)), []);\n\n  useEffect(() => {\n    register(api).catch(setError);\n  }, [setError]);\n\n  useEffect(() => hankoClient?.onSessionCreated(() => {\n    redirectToTodos()\n  }), [hankoClient, redirectToTodos]);\n\n  return <hanko-auth />;\n}\n\nexport default HankoAuth;\n"
  },
  {
    "path": "frontend/examples/nextjs/components/HankoProfile.tsx",
    "content": "import { register } from \"@teamhanko/hanko-elements\";\nimport { useEffect } from \"react\";\n\nconst api = process.env.NEXT_PUBLIC_HANKO_API!;\n\ninterface Props {\n  setError(error: Error): void;\n}\n\nfunction HankoProfile({ setError }: Props) {\n  useEffect(() => {\n    register(api).catch(setError);\n  }, [setError]);\n\n  return <hanko-profile />;\n}\n\nexport default HankoProfile;\n"
  },
  {
    "path": "frontend/examples/nextjs/components/SessionExpiredModal.tsx",
    "content": "import { forwardRef, useCallback } from \"react\";\nimport { useRouter } from \"next/router\";\n\nexport const SessionExpiredModal = forwardRef<HTMLDialogElement>(\n  (props, ref) => {\n    const router = useRouter();\n\n    const redirectToLogin = useCallback(() => {\n      router.push(\"/\");\n    }, [router]);\n\n    return (\n      <dialog ref={ref}>\n        Please login again.\n        <br />\n        <br />\n        <button onClick={redirectToLogin}>Login</button>\n      </dialog>\n    );\n  }\n);\n\nSessionExpiredModal.displayName = \"SessionExpiredModal\"\n"
  },
  {
    "path": "frontend/examples/nextjs/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  reactStrictMode: true\n}\n\nmodule.exports = nextConfig\n"
  },
  {
    "path": "frontend/examples/nextjs/package.json",
    "content": "{\n  \"name\": \"example-nextjs\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"next dev -p 8888\",\n    \"build\": \"next build\"\n  },\n  \"dependencies\": {\n    \"@teamhanko/hanko-elements\": \"^2.5.0\",\n    \"next\": \"^16.2.0\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^25.0.2\",\n    \"@types/react\": \"^18.3.1\",\n    \"@types/react-dom\": \"^18.3.1\",\n    \"typescript\": \"^5.9.3\"\n  }\n}\n"
  },
  {
    "path": "frontend/examples/nextjs/pages/_app.tsx",
    "content": "import \"../styles/index.css\";\nimport Head from 'next/head';\nimport type { AppProps } from \"next/app\";\n\nfunction MyApp({ Component, pageProps }: AppProps) {\n  return <>\n    <Head>\n      <title>Hanko Next.js Example</title>\n      <link rel=\"icon\" href=\"favicon.png\" />\n    </Head>\n    <Component {...pageProps} />\n  </>\n}\n\nexport default MyApp;\n"
  },
  {
    "path": "frontend/examples/nextjs/pages/index.tsx",
    "content": "import type { NextPage } from \"next\";\nimport styles from \"../styles/Todo.module.css\";\nimport React, { useState } from \"react\";\nimport HankoAuth from \"../components/HankoAuth\";\n\n\nconst Home: NextPage = () => {\n  const [error, setError] = useState<Error | null>(null);\n\n  return (\n    <div className={styles.content}>\n      <div className={styles.error}>{error?.message}</div>\n      <HankoAuth setError={setError} />\n    </div>\n  );\n};\n\nexport default Home;\n"
  },
  {
    "path": "frontend/examples/nextjs/pages/profile.tsx",
    "content": "import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { NextPage } from \"next\";\nimport { useRouter } from \"next/router\";\nimport styles from \"../styles/Todo.module.css\";\n\nimport { Hanko } from \"@teamhanko/hanko-elements\";\nimport { SessionExpiredModal } from \"../components/SessionExpiredModal\";\nimport HankoProfile from \"../components/HankoProfile\";\n\nconst api = process.env.NEXT_PUBLIC_HANKO_API!;\n\nconst Profile: NextPage = () => {\n  const router = useRouter();\n  const [hankoClient, setHankoClient] = useState<Hanko>();\n  const modalRef = useRef<HTMLDialogElement>(null);\n  const [error, setError] = useState<Error | null>(null);\n\n  const logout = () => {\n    hankoClient?.logout()\n      .catch((e) => {\n        setError(e);\n      });\n  };\n\n  const redirectToTodos = () => {\n    router.push(\"/todo\").catch((e) => setError(e));\n  };\n\n  const redirectToLogin = useCallback(() => {\n    router.push(\"/\").catch(setError)\n  }, [router]);\n\n  useEffect(() => {\n    if (!hankoClient) {\n      return;\n    }\n\n    hankoClient.validateSession().then(({is_valid}) => {\n      if (!is_valid) {\n        redirectToLogin();\n      }\n    }).catch(setError);\n  }, [hankoClient, redirectToLogin]);\n\n  useEffect(() => setHankoClient(new Hanko(api)), []);\n\n  useEffect(() => hankoClient?.onUserLoggedOut(() => {\n    redirectToLogin();\n  }), [hankoClient, redirectToLogin]);\n\n  useEffect(() => hankoClient?.onSessionExpired(() => {\n    modalRef.current?.showModal();\n  }), [hankoClient]);\n\n  return (\n    <>\n      <SessionExpiredModal ref={modalRef} />\n      <nav className={styles.nav}>\n        <button onClick={logout} className={styles.button}>\n          Logout\n        </button>\n        <button disabled className={styles.button}>\n          Profile\n        </button>\n        <button onClick={redirectToTodos} className={styles.button}>\n          Todos\n        </button>\n      </nav>\n      <div className={styles.content}>\n        <h1 className={styles.headline}>Profile</h1>\n        <div className={styles.error}>{error?.message}</div>\n        <HankoProfile setError={setError} />\n      </div>\n    </>\n  );\n};\n\nexport default Profile;\n"
  },
  {
    "path": "frontend/examples/nextjs/pages/todo.tsx",
    "content": "import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { NextPage } from \"next\";\nimport { useRouter } from \"next/router\";\nimport { TodoClient, Todos } from \"../util/TodoClient\";\nimport styles from \"../styles/Todo.module.css\";\nimport { SessionExpiredModal } from \"../components/SessionExpiredModal\";\nimport { Hanko } from \"@teamhanko/hanko-elements\";\n\nconst hankoAPI = process.env.NEXT_PUBLIC_HANKO_API!;\nconst todoAPI = process.env.NEXT_PUBLIC_TODO_API!;\n\nconst Todo: NextPage = () => {\n  const router = useRouter();\n  const todoClient = useMemo(() => new TodoClient(todoAPI), []);\n  const [hankoClient, setHankoClient] = useState<Hanko>();\n\n  const [todos, setTodos] = useState<Todos>([]);\n  const [description, setDescription] = useState<string>(\"\");\n  const [error, setError] = useState<Error | null>(null);\n\n  const modalRef = useRef<HTMLDialogElement>(null);\n\n  useEffect(() => {\n    setHankoClient(new Hanko(hankoAPI));\n  }, []);\n\n  const redirectToProfile = () => {\n    router.push(\"/profile\").catch(setError)\n  }\n\n  const redirectToLogin = useCallback(() => {\n    router.push(\"/\").catch(setError)\n  }, [router]);\n\n  const addTodo = (event: React.FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    const todo = {description, checked: false};\n\n    todoClient\n      .addTodo(todo)\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        setDescription(\"\");\n        listTodos();\n\n        return;\n      })\n      .catch((e) => {\n        setError(e);\n      });\n  };\n\n  const listTodos = useCallback(() => {\n    todoClient\n      .listTodos()\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        return res.json();\n      })\n      .then((todo) => {\n        if (todo) {\n          setTodos(todo);\n        }\n      })\n      .catch((e) => {\n        setError(e);\n      });\n  }, [todoClient]);\n\n  const patchTodo = (id: string, checked: boolean) => {\n    todoClient\n      .patchTodo(id, checked)\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        listTodos();\n\n        return;\n      })\n      .catch((e) => {\n        setError(e);\n      });\n  };\n\n  const deleteTodo = (id: string) => {\n    todoClient\n      .deleteTodo(id)\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        listTodos();\n\n        return;\n      })\n      .catch((e) => {\n        setError(e);\n      });\n  };\n\n  const logout = () => {\n    hankoClient?.logout()\n      .catch((e) => {\n        setError(e);\n      });\n  };\n\n  const changeDescription = (event: React.ChangeEvent<HTMLInputElement>) => {\n    setDescription(event.currentTarget.value);\n  };\n\n  const changeCheckbox = (event: React.ChangeEvent<HTMLInputElement>) => {\n    const {currentTarget} = event;\n    patchTodo(currentTarget.value, currentTarget.checked);\n  };\n\n  useEffect(() => {\n    if (!hankoClient) {\n      return;\n    }\n\n    hankoClient.validateSession().then(({is_valid}) => {\n      if (is_valid) {\n        listTodos();\n      } else {\n        redirectToLogin();\n      }\n    }).catch(setError);\n  }, [hankoClient, listTodos, redirectToLogin]);\n\n  useEffect(() => hankoClient?.onUserLoggedOut(() => {\n    redirectToLogin();\n  }), [hankoClient, redirectToLogin]);\n\n  useEffect(() => hankoClient?.onSessionExpired(() => {\n    modalRef.current?.showModal();\n  }), [hankoClient]);\n\n  return (\n    <>\n      <SessionExpiredModal ref={modalRef}/>\n      <nav className={styles.nav}>\n        <button onClick={logout} className={styles.button}>\n          Logout\n        </button>\n        <button onClick={redirectToProfile} className={styles.button}>\n          Profile\n        </button>\n        <button disabled className={styles.button}>\n          Todos\n        </button>\n      </nav>\n      <div className={styles.content}>\n        <h1 className={styles.headline}>Todos</h1>\n        <div className={styles.error}>{error?.message}</div>\n        <form onSubmit={addTodo} className={styles.form}>\n          <input\n            required\n            className={styles.input}\n            type={\"text\"}\n            value={description}\n            onChange={changeDescription}\n          />\n          <button type={\"submit\"} className={styles.button}>\n            +\n          </button>\n        </form>\n        <div className={styles.list}>\n          {todos.map((todo, index) => (\n            <div className={styles.item} key={index}>\n              <input\n                className={styles.checkbox}\n                id={todo.todoID}\n                type={\"checkbox\"}\n                value={todo.todoID}\n                checked={todo.checked}\n                onChange={changeCheckbox}\n              />\n              <label className={styles.description} htmlFor={todo.todoID}>\n                {todo.description}\n              </label>\n              <button className={styles.button} onClick={() => deleteTodo(todo.todoID!)}>\n                ×\n              </button>\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default Todo;\n"
  },
  {
    "path": "frontend/examples/nextjs/styles/Todo.module.css",
    "content": ".nav {\n    width: 100%;\n    padding: 10px;\n    opacity: 0.9;\n}\n\n.button {\n    font-size: 1rem;\n    border: none;\n    background: none;\n    cursor: pointer;\n}\n\n.button:disabled {\n    color: grey!important;\n    cursor: default;\n    text-decoration: none!important;\n}\n\n.nav .button:hover {\n    text-decoration: underline;\n}\n\n.nav .button {\n    color: white;\n    float: right;\n}\n\n.content {\n    padding: 24px;\n    border-radius: 17px;\n    color: black;\n    background-color: white;\n    width: 100%;\n    max-width: 500px;\n    min-width: 330px;\n    margin: 10vh auto;\n}\n\n.headline {\n    text-align: center;\n    margin-top: 0;\n}\n\n.form {\n    display: flex;\n    margin-bottom: 17px;\n}\n\n.form .input {\n    flex-grow: 1;\n    margin-right: 10px;\n}\n\n.form .button {\n    color: black;\n}\n\n.list {\n    display: flex;\n    flex-direction: column;\n    row-gap: 7px;\n}\n\n.item {\n    display: flex;\n    justify-content: space-between;\n    align-items: flex-start;\n    column-gap: 7px;\n}\n\n.description {\n    flex-grow: 1;\n    cursor: pointer;\n}\n\n.error {\n    color: red;\n    padding: 0 0 10px;\n}\n\n.input {\n    border: 1px solid black;\n    border-radius: 2.4px;\n}\n\n.checkbox {\n    margin-left: 0;\n    -webkit-appearance: none;\n    appearance: none;\n    background-color: #fff;\n    font: inherit;\n    color: currentColor;\n    width: 1em;\n    height: 1em;\n    border: 1px solid currentColor;\n    border-radius: 0.15em;\n    transform: translateY(-0.075em);\n    display: grid;\n    place-content: center;\n}\n\n.checkbox::before {\n    content: \"\";\n    width: 0.65em;\n    height: 0.65em;\n    transform: scale(0);\n    box-shadow: inset 1em 1em black;\n\n    transform-origin: bottom left;\n    clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);\n}\n\n.checkbox:checked::before {\n    transform: scale(1);\n}\n"
  },
  {
    "path": "frontend/examples/nextjs/styles/index.css",
    "content": "body {\n  color: white;\n  margin: 0;\n  background: url(\"../assets/bg.jpg\") no-repeat center center fixed;\n  background-size: cover;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n    sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhanko-auth::part(form-item) {\n  min-width: 100%; /* input fields and buttons are on top of each other  */\n}\n"
  },
  {
    "path": "frontend/examples/nextjs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"downlevelIteration\": true,\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"incremental\": true\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "frontend/examples/nextjs/util/TodoClient.ts",
    "content": "export interface Todo {\n  todoID?: string;\n  description: string;\n  checked: boolean;\n}\n\nexport type Todos = Todo[];\n\nexport class TodoClient {\n  api: string;\n\n  constructor(api: string) {\n    this.api = api;\n  }\n\n  addTodo(todo: Todo) {\n    return fetch(`${this.api}/todo`, {\n      method: \"POST\",\n      credentials: \"include\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify(todo),\n    });\n  }\n\n  listTodos() {\n    return fetch(`${this.api}/todo`, {\n      credentials: \"include\",\n    });\n  }\n\n  patchTodo(id: string, checked: boolean) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: \"PATCH\",\n      credentials: \"include\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({checked}),\n    });\n  }\n\n  deleteTodo(id: string) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: \"DELETE\",\n      credentials: \"include\",\n    });\n  }\n}\n"
  },
  {
    "path": "frontend/examples/react/.dockerignore",
    "content": "node_modules\n"
  },
  {
    "path": "frontend/examples/react/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/dist\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": "frontend/examples/react/Dockerfile",
    "content": "# pull official base image\nFROM node:16-alpine\n\n# set working directory\nWORKDIR /app\n\n# add `/app/node_modules/.bin` to $PATH\nENV PATH /app/node_modules/.bin:$PATH\n\n# install app dependencies\nCOPY package.json ./\nRUN npm install\nRUN npm install react-scripts@3.4.1 -g --silent\n\n# add app\nCOPY . ./\n\n# start app\nCMD [\"npm\", \"start\"]\n"
  },
  {
    "path": "frontend/examples/react/README.md",
    "content": "# Hanko React example\n\nThis is a [React](https://reactjs.org/) project bootstrapped with [Create React App](https://github.com/facebook/create-react-app).\n\n## Starting the app\n\n### Prerequisites\n\n- a running Hanko API (see the instructions on how to run the API [in Docker](../../../backend/README.md#Docker) or [from Source](../../../backend/README.md#from-source))\n- a running express backend (see the [README](../express) for the express backend)\n\n### Set up environment variables\n\nIn the `.env` file set up the correct environment variables:\n\n- `REACT_APP_HANKO_API`: this is the URL of the Hanko API (default: `http://localhost:8000`, can be customized using\n  the `server.public.address` option in the [backend configuration](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-public#address))\n- `REACT_APP_TODO_API`: this is the URL of the [express](../express) backend (default: `http://localhost:8002`)\n\n### Run development server\n\nRun `npm install` to install dependencies, then run `npm run start` for a development server. Navigate to `http://localhost:3000/`. The application will automatically reload if you change any of the source files.\n"
  },
  {
    "path": "frontend/examples/react/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"/favicon.png\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <meta\n      name=\"description\"\n      content=\"Web site created using create-react-app\"\n    />\n    <link rel=\"manifest\" href=\"/manifest.json\" />\n    <title>Hanko React Example</title>\n  </head>\n  <body>\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "frontend/examples/react/package.json",
    "content": "{\n  \"name\": \"example-react\",\n  \"private\": true,\n  \"dependencies\": {\n    \"@teamhanko/hanko-elements\": \"^2.5.0\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"react-router-dom\": \"7.13.1\"\n  },\n  \"scripts\": {\n    \"start\": \"vite --port 8888 --host\",\n    \"build\": \"vite build\"\n  },\n  \"browserslist\": {\n    \"production\": [\n      \">0.2%\",\n      \"not dead\",\n      \"not op_mini all\"\n    ],\n    \"development\": [\n      \"last 1 chrome version\",\n      \"last 1 firefox version\",\n      \"last 1 safari version\"\n    ]\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.27\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"5.1.4\",\n    \"typescript\": \"^5.9.3\",\n    \"vite\": \"^6.4.1\"\n  },\n  \"optionalDependencies\": {\n    \"@rollup/rollup-linux-x64-gnu\": \"*\"\n  },\n  \"overrides\": {\n    \"vite\": {\n      \"rollup\": \"npm:@rollup/wasm-node\"\n    }\n  }\n\n}\n"
  },
  {
    "path": "frontend/examples/react/public/manifest.json",
    "content": "{\n  \"short_name\": \"React App\",\n  \"name\": \"Hanko React Example\",\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "frontend/examples/react/public/robots.txt",
    "content": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
  },
  {
    "path": "frontend/examples/react/src/HankoAuth.tsx",
    "content": "import {\n  useCallback,\n  useEffect,\n  useState,\n  useMemo,\n} from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { Hanko, register } from \"@teamhanko/hanko-elements\";\nimport styles from \"./Todo.module.css\";\n\nconst api = import.meta.env.VITE_HANKO_API!;\n\nfunction HankoAuth() {\n  const navigate = useNavigate();\n  const [error, setError] = useState<Error | null>(null);\n  const hankoClient = useMemo(() => new Hanko(api), []);\n\n  const redirectToTodos = useCallback(() => {\n    navigate(\"/todo\", { replace: true });\n  }, [navigate]);\n\n  useEffect(() => {\n    register(api).catch(setError);\n  }, []);\n\n  useEffect(\n    () => hankoClient.onSessionCreated(() => redirectToTodos()),\n    [hankoClient, redirectToTodos]\n  );\n\n  return (\n    <div className={styles.content}>\n      <div className={styles.error}>{error?.message}</div>\n      <hanko-auth />\n    </div>\n  );\n}\n\nexport default HankoAuth;\n"
  },
  {
    "path": "frontend/examples/react/src/HankoProfile.tsx",
    "content": "import {\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { Hanko, register } from \"@teamhanko/hanko-elements\";\nimport styles from \"./Todo.module.css\";\nimport { useNavigate } from \"react-router-dom\";\nimport { SessionExpiredModal } from \"./SessionExpiredModal\";\n\nconst api = import.meta.env.VITE_HANKO_API!;\n\nfunction HankoProfile() {\n  const navigate = useNavigate();\n  const hankoClient = useMemo(() => new Hanko(api), []);\n  const [error, setError] = useState<Error | null>(null);\n  const modalRef = useRef<HTMLDialogElement>(null);\n\n  const logout = () => {\n    hankoClient.logout().catch(setError);\n  };\n\n  const redirectToTodo = () => {\n    navigate(\"/todo\");\n  };\n\n  const redirectToLogin = useCallback(() => {\n    navigate(\"/\");\n  }, [navigate]);\n\n  useEffect(() => {\n    register(api).catch(setError);\n  }, []);\n\n  useEffect(() => {\n    hankoClient.validateSession().then(({is_valid}) => {\n      if (!is_valid) {\n        redirectToLogin();\n      }\n    }).catch(setError);\n  }, [hankoClient, redirectToLogin]);\n\n\n  useEffect(\n    () => hankoClient.onUserLoggedOut(() => redirectToLogin()),\n    [hankoClient, redirectToLogin]\n  );\n\n  useEffect(\n    () => hankoClient.onSessionExpired(() => modalRef.current?.showModal()),\n    [hankoClient]\n  );\n\n  return (\n    <>\n      <SessionExpiredModal ref={modalRef} />\n      <nav className={styles.nav}>\n        <button onClick={logout} className={styles.button}>\n          Logout\n        </button>\n        <button disabled className={styles.button}>\n          Profile\n        </button>\n        <button onClick={redirectToTodo} className={styles.button}>\n          Todos\n        </button>\n      </nav>\n      <div className={styles.content}>\n        <h1 className={styles.headline}>Profile</h1>\n        <div className={styles.error}>{error?.message}</div>\n        <hanko-profile />\n      </div>\n    </>\n  );\n}\n\nexport default HankoProfile;\n"
  },
  {
    "path": "frontend/examples/react/src/SessionExpiredModal.tsx",
    "content": "import { forwardRef, useCallback } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\n\nexport const SessionExpiredModal = forwardRef<HTMLDialogElement>(\n  (props, ref) => {\n    const navigate = useNavigate();\n\n    const redirectToLogin = useCallback(() => {\n      navigate(\"/\");\n    }, [navigate]);\n\n    return (\n      <dialog ref={ref}>\n        Please login again.\n        <br />\n        <br />\n        <button onClick={redirectToLogin}>Login</button>\n      </dialog>\n    );\n  }\n);\n"
  },
  {
    "path": "frontend/examples/react/src/Todo.module.css",
    "content": ".nav {\n  width: 100%;\n  padding: 10px;\n  opacity: 0.9;\n}\n\n.button {\n  font-size: 1rem;\n  border: none;\n  background: none;\n  cursor: pointer;\n}\n\n.button:disabled {\n  color: grey!important;\n  cursor: default;\n  text-decoration: none!important;\n}\n\n.nav .button:hover {\n  text-decoration: underline;\n}\n\n.nav .button {\n  color: white;\n  float: right;\n}\n\n.content {\n  padding: 24px;\n  border-radius: 17px;\n  color: black;\n  background-color: white;\n  width: 100%;\n  max-width: 500px;\n  min-width: 330px;\n  margin: 10vh auto;\n}\n\n.headline {\n  text-align: center;\n  margin-top: 0;\n}\n\n.form {\n  display: flex;\n  margin-bottom: 17px;\n}\n\n.form .input {\n  flex-grow: 1;\n  margin-right: 10px;\n}\n\n.form .button {\n  color: black;\n}\n\n.list {\n  display: flex;\n  flex-direction: column;\n  row-gap: 7px;\n}\n\n.item {\n  display: flex;\n  justify-content: space-between;\n  align-items: flex-start;\n  column-gap: 7px;\n}\n\n.description {\n  flex-grow: 1;\n  cursor: pointer;\n}\n\n.error {\n  color: red;\n  padding: 0 0 10px;\n}\n\n.input {\n  border: 1px solid black;\n  border-radius: 2.4px;\n}\n\n.checkbox {\n  margin-left: 0;\n  -webkit-appearance: none;\n  appearance: none;\n  background-color: #fff;\n  font: inherit;\n  color: currentColor;\n  width: 1em;\n  height: 1em;\n  border: 1px solid currentColor;\n  border-radius: 0.15em;\n  transform: translateY(-0.075em);\n  display: grid;\n  place-content: center;\n}\n\n.checkbox::before {\n  content: \"\";\n  width: 0.65em;\n  height: 0.65em;\n  transform: scale(0);\n  box-shadow: inset 1em 1em black;\n\n  transform-origin: bottom left;\n  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);\n}\n\n.checkbox:checked::before {\n  transform: scale(1);\n}\n"
  },
  {
    "path": "frontend/examples/react/src/Todo.tsx",
    "content": "import{\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { TodoClient, type Todos } from \"./TodoClient\";\nimport styles from \"./Todo.module.css\";\nimport { Hanko } from \"@teamhanko/hanko-elements\";\nimport { SessionExpiredModal } from \"./SessionExpiredModal\";\n\nconst todoAPI = import.meta.env.VITE_TODO_API!;\nconst hankoAPI = import.meta.env.VITE_HANKO_API!;\n\nfunction Todo() {\n  const navigate = useNavigate();\n  const hankoClient = useMemo(() => new Hanko(hankoAPI), []);\n  const [todos, setTodos] = useState<Todos>([]);\n  const [description, setDescription] = useState<string>(\"\");\n  const [error, setError] = useState<Error | null>(null);\n  const todoClient = useMemo(() => new TodoClient(todoAPI), []);\n  const modalRef = useRef<HTMLDialogElement | null>(null);\n\n  const redirectToLogin = useCallback(() => {\n    navigate(\"/\");\n  }, [navigate]);\n\n  const redirectToProfile = () => {\n    navigate(\"/profile\");\n  };\n\n  const addTodo = (event: React.FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    const todo = { description, checked: false };\n\n    todoClient\n      .addTodo(todo)\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        setDescription(\"\");\n        listTodos();\n\n        return;\n      })\n      .catch(setError);\n  };\n\n  const listTodos = useCallback(() => {\n    todoClient\n      .listTodos()\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        return res.json();\n      })\n      .then((todo) => {\n        if (todo) {\n          setTodos(todo);\n        }\n      })\n      .catch(setError);\n  }, [todoClient]);\n\n  const patchTodo = (id: string, checked: boolean) => {\n    todoClient\n      .patchTodo(id, checked)\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        listTodos();\n\n        return;\n      })\n      .catch(setError);\n  };\n\n  const deleteTodo = (id: string) => {\n    todoClient\n      .deleteTodo(id)\n      .then((res) => {\n        if (res.status === 401) {\n          modalRef.current?.showModal();\n          return;\n        }\n\n        listTodos();\n\n        return;\n      })\n      .catch(setError);\n  };\n\n  const logout = () => {\n    hankoClient.logout().catch(setError);\n  };\n\n  const changeDescription = (event: React.ChangeEvent<HTMLInputElement>) => {\n    setDescription(event.currentTarget.value);\n  };\n\n  const changeCheckbox = (event: React.ChangeEvent<HTMLInputElement>) => {\n    const { currentTarget } = event;\n    patchTodo(currentTarget.value, currentTarget.checked);\n  };\n\n  useEffect(() => {\n    hankoClient.validateSession().then(({is_valid}) => {\n      if (is_valid) {\n        listTodos();\n      } else {\n        redirectToLogin();\n      }\n    }).catch(setError);\n  }, [hankoClient, listTodos, redirectToLogin]);\n\n  useEffect(\n    () => hankoClient.onUserLoggedOut(() => redirectToLogin()),\n    [hankoClient, redirectToLogin]\n  );\n\n  useEffect(\n    () => hankoClient.onSessionExpired(() => modalRef.current?.showModal()),\n    [hankoClient]\n  );\n\n  return (\n    <>\n      <SessionExpiredModal ref={modalRef} />\n      <nav className={styles.nav}>\n        <button onClick={logout} className={styles.button}>\n          Logout\n        </button>\n        <button onClick={redirectToProfile} className={styles.button}>\n          Profile\n        </button>\n        <button disabled className={styles.button}>\n          Todos\n        </button>\n      </nav>\n      <div className={styles.content}>\n        <h1 className={styles.headline}>Todos</h1>\n        <div className={styles.error}>{error?.message}</div>\n        <form onSubmit={addTodo} className={styles.form}>\n          <input\n            required\n            className={styles.input}\n            type={\"text\"}\n            value={description}\n            onChange={changeDescription}\n          />\n          <button type={\"submit\"} className={styles.button}>\n            +\n          </button>\n        </form>\n        <div className={styles.list}>\n          {todos.map((todo, index) => (\n            <div className={styles.item} key={index}>\n              <input\n                className={styles.checkbox}\n                id={todo.todoID}\n                type={\"checkbox\"}\n                value={todo.todoID}\n                checked={todo.checked}\n                onChange={changeCheckbox}\n              />\n              <label className={styles.description} htmlFor={todo.todoID}>\n                {todo.description}\n              </label>\n              <button\n                className={styles.button}\n                onClick={() => deleteTodo(todo.todoID!)}\n              >\n                ×\n              </button>\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  );\n}\n\nexport default Todo;\n"
  },
  {
    "path": "frontend/examples/react/src/TodoClient.ts",
    "content": "export interface Todo {\n  todoID?: string;\n  description: string;\n  checked: boolean;\n}\n\nexport type Todos = Todo[];\n\nexport class TodoClient {\n  api: string;\n\n  constructor(api: string) {\n    this.api = api;\n  }\n\n  addTodo(todo: Todo) {\n    return fetch(`${this.api}/todo`, {\n      method: \"POST\",\n      credentials: \"include\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify(todo),\n    });\n  }\n\n  listTodos() {\n    return fetch(`${this.api}/todo`, {\n      credentials: \"include\",\n    });\n  }\n\n  patchTodo(id: string, checked: boolean) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: \"PATCH\",\n      credentials: \"include\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({checked}),\n    });\n  }\n\n  deleteTodo(id: string) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: \"DELETE\",\n      credentials: \"include\",\n    });\n  }\n}\n"
  },
  {
    "path": "frontend/examples/react/src/index.css",
    "content": "body {\n  color: white;\n  margin: 0;\n  background: url(\"../assets/bg.jpg\") no-repeat center center fixed;\n  background-size: cover;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n    \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n    sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhanko-auth::part(form-item) {\n  min-width: 100%; /* input fields and buttons are on top of each other  */\n}\n"
  },
  {
    "path": "frontend/examples/react/src/index.tsx",
    "content": "import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport { BrowserRouter, Route, Routes } from \"react-router-dom\";\nimport HankoAuth from \"./HankoAuth\";\nimport Todo from \"./Todo\";\nimport \"./index.css\";\nimport HankoProfile from \"./HankoProfile\";\n\nconst root = ReactDOM.createRoot(\n  document.getElementById(\"root\") as HTMLElement\n);\n\nroot.render(\n  <React.StrictMode>\n    <BrowserRouter>\n      <Routes>\n        <Route path=\"/\" element={<HankoAuth />} />\n        <Route path=\"/todo\" element={<Todo />} />\n        <Route path=\"/profile\" element={<HankoProfile />} />\n      </Routes>\n    </BrowserRouter>\n  </React.StrictMode>\n);\n"
  },
  {
    "path": "frontend/examples/react/src/react-app-env.d.ts",
    "content": "/// <reference types=\"react-scripts\" />\n"
  },
  {
    "path": "frontend/examples/react/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n    \"target\": \"ES2022\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"types\": [\"vite/client\"],\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"verbatimModuleSyntax\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"erasableSyntaxOnly\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noUncheckedSideEffectImports\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "frontend/examples/react/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.node.json\" }\n  ]\n}\n"
  },
  {
    "path": "frontend/examples/react/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n    \"target\": \"ES2023\",\n    \"lib\": [\"ES2023\"],\n    \"module\": \"ESNext\",\n    \"types\": [\"node\"],\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"verbatimModuleSyntax\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"erasableSyntaxOnly\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noUncheckedSideEffectImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "frontend/examples/react/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "frontend/examples/react/vite.config.js",
    "content": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig(() => {\n  return {\n    plugins: [react()],\n  };\n});\n"
  },
  {
    "path": "frontend/examples/vue/.dockerignore",
    "content": "node_modules\n"
  },
  {
    "path": "frontend/examples/vue/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\n.DS_Store\ndist\ndist-ssr\ncoverage\n*.local\n\n/cypress/videos/\n/cypress/screenshots/\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "frontend/examples/vue/.prettierrc.json",
    "content": "{}"
  },
  {
    "path": "frontend/examples/vue/.vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"Vue.volar\",\n    \"EditorConfig.EditorConfig\",\n    \"esbenp.prettier-vscode\"\n  ]\n}\n"
  },
  {
    "path": "frontend/examples/vue/Dockerfile",
    "content": "# pull official base image\nFROM node:16-alpine\n\n# set working directory\nWORKDIR /app\n\n# add `/app/node_modules/.bin` to $PATH\nENV PATH /app/node_modules/.bin:$PATH\n\n# install app dependencies\nCOPY package.json ./\nRUN npm install\n\n# add app\nCOPY . ./\n\n# start app\nCMD [\"npm\", \"start\"]\n"
  },
  {
    "path": "frontend/examples/vue/README.md",
    "content": "# Hanko Vue example\n\nThis is a [Vue](https://vuejs.org/) project bootstrapped with Vue version 3.2.39.\n\n## Starting the app\n\n### Prerequisites\n\n- a running Hanko API (see the instructions on how to run the API [in Docker](../../../backend/README.md#Docker) or [from Source](../../../backend/README.md#from-source))\n- a running express backend (see the [README](../express) for the express backend)\n\n### Set up environment variables\n\nIn the `.env` file set up the correct environment variables:\n\n- `VITE_HANKO_API`: this is the URL of the Hanko API (default: `http://localhost:8000`, can be customized using\n  the `server.public.address` option in the [backend configuration](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-public#address))\n- `VITE_TODO_API`: this is the URL of the [express](../express) backend (default: `http://localhost:8002`)\n\n### Run development server\n\nRun `npm install` to install dependencies, then run `npm run start` for a development server. Navigate to `http://localhost:8888/`. The application will automatically reload if you change any of the source files.\n"
  },
  {
    "path": "frontend/examples/vue/env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "frontend/examples/vue/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/favicon.png\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Hanko Vue Example</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "frontend/examples/vue/package.json",
    "content": "{\n  \"name\": \"example-vue\",\n  \"scripts\": {\n    \"start\": \"vite --port 8888 --host\",\n    \"build\": \"vite build\"\n  },\n  \"dependencies\": {\n    \"@teamhanko/hanko-elements\": \"^2.5.0\",\n    \"vue\": \"^3.5.30\",\n    \"vue-router\": \"^5.0.3\"\n  },\n  \"devDependencies\": {\n    \"@tsconfig/node24\": \"^24.0.4\",\n    \"@types/node\": \"^24.10.1\",\n    \"@vitejs/plugin-vue\": \"^6.0.2\",\n    \"@vue/tsconfig\": \"^0.8.1\",\n    \"jiti\": \"^2.6.1\",\n    \"prettier\": \"3.8.1\",\n    \"typescript\": \"~5.9.0\",\n    \"vite\": \"^7.2.4\",\n    \"vue-tsc\": \"^3.2.6\"\n  },\n  \"optionalDependencies\": {\n    \"@rollup/rollup-linux-x64-gnu\": \"*\"\n  },\n  \"overrides\": {\n    \"vite\": {\n      \"rollup\": \"npm:@rollup/wasm-node\"\n    }\n  }\n}\n"
  },
  {
    "path": "frontend/examples/vue/src/App.vue",
    "content": "<script setup lang=\"ts\">\nimport { RouterView } from \"vue-router\";\nimport { register } from \"@teamhanko/hanko-elements\";\nimport { onMounted } from \"vue\";\n\nconst hankoAPI = import.meta.env.VITE_HANKO_API;\n\nonMounted(() => {\n  register(hankoAPI);\n});\n</script>\n\n<template>\n  <RouterView />\n</template>\n\n<style>\n.content {\n  padding: 24px;\n  border-radius: 17px;\n  color: black;\n  background-color: white;\n  width: 100%;\n  max-width: 500px;\n  min-width: 330px;\n  margin: 10vh auto;\n}\n\n.error {\n  color: red;\n  padding: 0 0 10px;\n}\n\n\n.button {\n  font-size: 1rem;\n  border: none;\n  background: none;\n  cursor: pointer;\n}\n\n.button:disabled {\n  color: grey !important;\n  cursor: default;\n  text-decoration: none !important;\n}\n\n.nav {\n  width: 100%;\n  padding: 10px;\n  opacity: 0.9;\n}\n\n\n.nav .button:hover {\n  text-decoration: underline;\n}\n\n.nav .button {\n  color: white;\n  float: right;\n}\n</style>\n"
  },
  {
    "path": "frontend/examples/vue/src/assets/base.css",
    "content": "body {\n  color: white;\n  margin: 0;\n  background: url(\"bg.jpg\") no-repeat center center fixed;\n  background-size: cover;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n  'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n  sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhanko-auth::part(form-item) {\n  min-width: 100%; /* input fields and buttons are on top of each other  */\n}\n"
  },
  {
    "path": "frontend/examples/vue/src/components/SessionExpiredModal.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\";\nimport { useRouter } from \"vue-router\";\n\nconst router = useRouter();\nconst modal = ref<HTMLDialogElement>();\nconst error = ref<Error>();\n\nconst show = () => {\n  modal.value?.showModal();\n};\n\nconst redirectToLogin = () => {\n  router.push(\"/\").catch((e) => (error.value = e));\n};\n\ndefineExpose({\n  show,\n});\n</script>\n\n<template>\n  <hanko-events @onSessionExpired=\"show()\"></hanko-events>\n  <dialog ref=\"modal\">\n    <div class=\"error\">{{ error?.message }}</div>\n    Please login again.<br /><br />\n    <button @click=\"redirectToLogin()\">Login</button>\n  </dialog>\n</template>\n"
  },
  {
    "path": "frontend/examples/vue/src/main.ts",
    "content": "import { createApp } from \"vue\";\nimport App from \"./App.vue\";\nimport router from \"./router\";\n\nimport \"./assets/base.css\";\n\nconst app = createApp(App);\n\napp.use(router);\n\napp.mount(\"#app\");\n"
  },
  {
    "path": "frontend/examples/vue/src/router/index.ts",
    "content": "import { createRouter, createWebHistory } from \"vue-router\";\nimport LoginView from \"../views/LoginView.vue\";\nimport TodoView from \"../views/TodoView.vue\";\nimport ProfileView from \"../views/ProfileView.vue\";\n\nconst router = createRouter({\n  history: createWebHistory(import.meta.env.BASE_URL),\n  routes: [\n    {\n      path: \"/\",\n      name: \"login\",\n      component: LoginView,\n    },\n    {\n      path: \"/todo\",\n      name: \"todo\",\n      component: TodoView,\n    },\n    {\n      path: \"/profile\",\n      name: \"profile\",\n      component: ProfileView,\n    },\n  ],\n});\n\nexport default router;\n"
  },
  {
    "path": "frontend/examples/vue/src/utils/TodoClient.ts",
    "content": "export interface Todo {\n  todoID?: string;\n  description: string;\n  checked: boolean;\n}\n\nexport type Todos = Todo[];\n\nexport class TodoClient {\n  api: string;\n\n  constructor(api: string) {\n    this.api = api;\n  }\n\n  addTodo(todo: Todo) {\n    return fetch(`${this.api}/todo`, {\n      method: \"POST\",\n      credentials: \"include\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify(todo),\n    });\n  }\n\n  listTodos() {\n    return fetch(`${this.api}/todo`, {\n      credentials: \"include\",\n    });\n  }\n\n  patchTodo(id: string, checked: boolean) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: \"PATCH\",\n      credentials: \"include\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({ checked }),\n    });\n  }\n\n  deleteTodo(id: string) {\n    return fetch(`${this.api}/todo/${id}`, {\n      method: \"DELETE\",\n      credentials: \"include\",\n    });\n  }\n}\n"
  },
  {
    "path": "frontend/examples/vue/src/views/LoginView.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\";\nimport router from \"@/router\";\n\nconst error = ref<Error>();\n\nconst redirectToTodos = () => {\n  router.push(\"/todo\").catch((e) => (error.value = e));\n};\n</script>\n\n<template>\n  <main class=\"content\">\n    <div class=\"error\">{{ error?.message }}</div>\n    <hanko-auth @onSessionCreated=\"redirectToTodos()\" />\n  </main>\n</template>\n"
  },
  {
    "path": "frontend/examples/vue/src/views/ProfileView.vue",
    "content": "<script setup lang=\"ts\">\nimport { useRouter } from \"vue-router\";\nimport { onMounted, ref } from \"vue\";\nimport OnSessionExpiredModal from \"@/components/SessionExpiredModal.vue\";\nimport { Hanko } from \"@teamhanko/hanko-frontend-sdk\";\n\nconst router = useRouter();\n\nconst hankoAPI = import.meta.env.VITE_HANKO_API;\nconst hankoClient = new Hanko(hankoAPI);\n\nconst error = ref<Error>();\n\nconst redirectToLogin = () => {\n  router.push(\"/\").catch((e) => (error.value = e));\n};\n\nconst redirectToTodos = () => {\n  router.push(\"/todo\").catch((e) => (error.value = e));\n};\n\nconst logout = () => {\n  hankoClient.logout().catch((e) => (error.value = e));\n};\n\nonMounted(async () => {\n  const {is_valid} = await hankoClient.validateSession();\n  if (!is_valid) {\n    redirectToLogin();\n  }\n});\n</script>\n\n<template>\n  <on-session-expired-modal></on-session-expired-modal>\n  <nav class=\"nav\">\n    <button @click.prevent=\"logout\" class=\"button\">Logout</button>\n    <button disabled class=\"button\">Profile</button>\n    <button @click.prevent=\"redirectToTodos\" class=\"button\">Todos</button>\n  </nav>\n  <main class=\"content\">\n    <div class=\"error\">{{ error?.message }}</div>\n      <hanko-profile @onUserLoggedOut=\"redirectToLogin\" />\n  </main>\n</template>\n"
  },
  {
    "path": "frontend/examples/vue/src/views/TodoView.vue",
    "content": "<script setup lang=\"ts\">\nimport { useRouter } from \"vue-router\";\nimport { onMounted, ref } from \"vue\";\nimport OnSessionExpiredModal from \"@/components/SessionExpiredModal.vue\";\nimport type { Todos } from \"@/utils/TodoClient\";\nimport { Hanko } from \"@teamhanko/hanko-frontend-sdk\";\nimport { TodoClient } from \"@/utils/TodoClient\";\n\nconst router = useRouter();\n\nconst todoAPI = import.meta.env.VITE_TODO_API;\nconst todoClient = new TodoClient(todoAPI);\n\nconst hankoAPI = import.meta.env.VITE_HANKO_API;\nconst hankoClient = new Hanko(hankoAPI);\n\nconst modal = ref<typeof SessionExpiredModal>();\nconst error = ref<Error>();\nconst todos = ref<Todos>([]);\nconst description = ref(\"\");\n\nconst changeDescription = (event: any) => {\n  description.value = event.currentTarget.value;\n};\n\nconst changeCheckbox = (event: any) => {\n  const { currentTarget } = event;\n  patchTodo(currentTarget.value, currentTarget.checked);\n};\n\nconst addTodo = () => {\n  const todo = { description: description.value, checked: false };\n\n  todoClient\n    .addTodo(todo)\n    .then((res) => {\n      if (res.status === 401) {\n        modal.value?.show();\n        return;\n      }\n\n      description.value = \"\";\n      listTodos();\n\n      return;\n    })\n    .catch((e) => (error.value = e));\n};\n\nconst listTodos = () => {\n  todoClient\n    .listTodos()\n    .then((res) => {\n      if (res.status === 401) {\n        modal.value?.show();\n        return;\n      }\n\n      return res.json();\n    })\n    .then((todo) => {\n      if (todo) {\n        todos.value = todo;\n      }\n    })\n    .catch((e) => (error.value = e));\n};\n\nconst patchTodo = (id: string, checked: boolean) => {\n  todoClient\n    .patchTodo(id, checked)\n    .then((res) => {\n      if (res.status === 401) {\n        modal.value?.show();\n        return;\n      }\n\n      listTodos();\n\n      return;\n    })\n    .catch((e) => (error.value = e));\n};\n\nconst deleteTodo = (id: string) => {\n  todoClient\n    .deleteTodo(id)\n    .then((res) => {\n      if (res.status === 401) {\n        modal.value?.show();\n        return;\n      }\n\n      listTodos();\n\n      return;\n    })\n    .catch((e) => (error.value = e));\n};\n\nconst redirectToLogin = () => {\n  router.push({ path: \"/\" }).catch((e) => (error.value = e));\n};\n\nconst redirectToProfile = () => {\n  router.push(\"/profile\").catch((e) => (error.value = e));\n};\n\nconst logout = () => {\n  hankoClient.logout().catch((e) => (error.value = e));\n};\n\nonMounted(async () => {\n  const {is_valid} = await hankoClient.validateSession();\n  if (is_valid) {\n    listTodos();\n  } else {\n    redirectToLogin();\n  }\n});\n</script>\n\n<template>\n  <hanko-events @onUserLoggedOut=\"redirectToLogin\"></hanko-events>\n  <on-session-expired-modal ref=\"modal\"></on-session-expired-modal>\n  <nav class=\"nav\">\n    <button @click.prevent=\"logout\" class=\"button\">Logout</button>\n    <button @click.prevent=\"redirectToProfile\" class=\"button\">Profile</button>\n    <button disabled class=\"button\">Todos</button>\n  </nav>\n  <div class=\"content\">\n    <h1 class=\"headline\">Todos</h1>\n    <div class=\"error\">{{ error?.message }}</div>\n    <form @submit.prevent=\"addTodo\" class=\"form\">\n      <input\n        required\n        class=\"input\"\n        type=\"text\"\n        :value=\"description\"\n        @change=\"changeDescription\"\n      />\n      <button type=\"submit\" class=\"button\">+</button>\n    </form>\n    <div class=\"list\">\n      <div v-for=\"(todo, index) in todos\" class=\"item\" :key=\"index\">\n        <input\n          class=\"checkbox\"\n          :id=\"todo.todoID\"\n          type=\"checkbox\"\n          :value=\"todo.todoID\"\n          :checked=\"todo.checked\"\n          @change=\"changeCheckbox\"\n        />\n        <label class=\"description\" :for=\"todo.todoID\">{{\n          todo.description\n        }}</label>\n        <button class=\"button\" @click=\"() => deleteTodo(todo.todoID!)\">\n          ×\n        </button>\n      </div>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.nav {\n  width: 100%;\n  padding: 10px;\n  opacity: 0.9;\n}\n\n.button {\n  font-size: 1rem;\n  border: none;\n  background: none;\n  cursor: pointer;\n}\n\n.button:disabled {\n  color: grey !important;\n  cursor: default;\n  text-decoration: none !important;\n}\n\n.nav .button:hover {\n  text-decoration: underline;\n}\n\n.nav .button {\n  color: white;\n  float: right;\n}\n\n.content {\n  padding: 24px;\n  border-radius: 17px;\n  color: black;\n  background-color: white;\n  width: 100%;\n  max-width: 500px;\n  min-width: 330px;\n  margin: 10vh auto;\n}\n\n.headline {\n  text-align: center;\n  margin-top: 0;\n}\n\n.form {\n  display: flex;\n  margin-bottom: 17px;\n}\n\n.form .input {\n  flex-grow: 1;\n  margin-right: 10px;\n}\n\n.form .button {\n  color: black;\n}\n\n.list {\n  display: flex;\n  flex-direction: column;\n  row-gap: 7px;\n}\n\n.item {\n  display: flex;\n  justify-content: space-between;\n  align-items: flex-start;\n  column-gap: 7px;\n}\n\n.description {\n  flex-grow: 1;\n  cursor: pointer;\n}\n\n.error {\n  color: red;\n  padding: 0 0 10px;\n}\n\n.input {\n  border: 1px solid black;\n  border-radius: 2.4px;\n}\n\n.checkbox {\n  margin-left: 0;\n  -webkit-appearance: none;\n  appearance: none;\n  background-color: #fff;\n  font: inherit;\n  color: currentColor;\n  width: 1em;\n  height: 1em;\n  border: 1px solid currentColor;\n  border-radius: 0.15em;\n  transform: translateY(-0.075em);\n  display: grid;\n  place-content: center;\n}\n\n.checkbox::before {\n  content: \"\";\n  width: 0.65em;\n  height: 0.65em;\n  transform: scale(0);\n  box-shadow: inset 1em 1em black;\n\n  transform-origin: bottom left;\n  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);\n}\n\n.checkbox:checked::before {\n  transform: scale(1);\n}\n</style>\n"
  },
  {
    "path": "frontend/examples/vue/tsconfig.app.json",
    "content": "{\n  \"extends\": \"@vue/tsconfig/tsconfig.dom.json\",\n  \"include\": [\"env.d.ts\", \"src/**/*\", \"src/**/*.vue\"],\n  \"compilerOptions\": {\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  },\n\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.node.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "frontend/examples/vue/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.node.json\"\n    },\n    {\n      \"path\": \"./tsconfig.app.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "frontend/examples/vue/tsconfig.node.json",
    "content": "{\n  \"extends\": \"@tsconfig/node24/tsconfig.json\",\n  \"include\": [\n    \"vite.config.*\"\n  ],\n  \"compilerOptions\": {\n    \"noEmit\": true,\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Bundler\",\n    \"types\": [\"node\"]\n  }\n}\n"
  },
  {
    "path": "frontend/examples/vue/vite.config.ts",
    "content": "import { fileURLToPath, URL } from \"node:url\";\n\nimport { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [\n    vue({\n      template: {\n        compilerOptions: { isCustomElement: (tag) => tag.startsWith(\"hanko-\") },\n      },\n    }),\n  ],\n  resolve: {\n    alias: {\n      \"@\": fileURLToPath(new URL(\"./src\", import.meta.url)),\n    },\n  },\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/.dockerignore",
    "content": "node_modules\ndist\n"
  },
  {
    "path": "frontend/frontend-sdk/.eslintignore",
    "content": "node_modules\n"
  },
  {
    "path": "frontend/frontend-sdk/.eslintrc.cjs",
    "content": "module.exports = {\n  'env': {\n    'browser': true,\n    'es2021': true,\n    'node': true,\n  },\n  'extends': [\n    'eslint:recommended',\n    'google',\n    'preact',\n    'plugin:promise/recommended',\n    'plugin:prettier/recommended',\n  ],\n  'parser': '@typescript-eslint/parser',\n  'parserOptions': {\n    'ecmaVersion': 'latest',\n    'sourceType': 'module',\n    \"project\": \"tsconfig.json\",\n    \"tsconfigRootDir\": \".\",\n  },\n  'plugins': [\n    '@typescript-eslint'\n  ]\n};\n"
  },
  {
    "path": "frontend/frontend-sdk/.gitignore",
    "content": "coverage\n"
  },
  {
    "path": "frontend/frontend-sdk/.nvmrc",
    "content": "v20.16.0\n"
  },
  {
    "path": "frontend/frontend-sdk/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Hanko GmbH\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "frontend/frontend-sdk/README.md",
    "content": "# hanko-frontend-sdk\n\nThis package utilizes the [Hanko API](https://github.com/teamhanko/hanko/blob/main/backend/README.md) to provide\nfunctionality that allows an easier UI integration. It is meant for use in browsers only.\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Options](#options)\n- [Session Events](#session-events)\n- [Session Management](#session-management)\n    - [Getting the User Object](#getting-the-user-object)\n    - [Validating a Session](#validating-a-session)\n    - [Getting the Session Token](#getting-the-session-token)\n    - [Logging out a User](#logging-out-a-user)\n- [Translation of Outgoing Emails](#translation-of-outgoing-emails)\n- [Custom Session Claim Type Safety](#custom-session-claim-type-safety)\n- [FlowAPI](#flowapi)\n    - [Initializing a New Flow](#initializing-a-new-flow)\n    - [Understanding the State Object](#understanding-the-state-object)\n    - [Action Availability](#action-availability)\n    - [Accessing Action Inputs](#accessing-action-inputs)\n    - [Running an Action](#running-an-action)\n    - [Event Handlers](#event-handlers)\n    - [Controlling the AfterStateChanged Event](#controlling-the-afterstatechanged-event)\n    - [Auto-Steps](#auto-steps)\n    - [Error Handling](#error-handling)\n    - [Caching Flow State](#caching-flow-state)\n- [Bugs](#bugs)\n- [Documentation](#documentation)\n- [License](#license)\n\n## Installation\n\n```shell\n# npm\nnpm install @teamhanko/hanko-frontend-sdk\n\n# yarn\nyarn add @teamhanko/hanko-frontend-sdk\n\n# pnpm\npnpm install @teamhanko/hanko-frontend-sdk\n```\n\n## Usage\n\nImport as a module:\n\n```typescript\nimport { Hanko } from \"@teamhanko/hanko-frontend-sdk\"\n\nconst hanko = new Hanko(\"http://localhost:3000\")\n```\n\nWith a script tag via CDN:\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/@teamhanko/hanko-frontend-sdk/dist/sdk.umd.js\"></script>\n\n<script>\n    const hanko = new hankoFrontendSdk.Hanko(\"http://localhost:3000\")\n    ...\n</script>\n```\n\n### Options\n\nYou can pass certain options, when creating a new `Hanko` instance:\n\n```js\nconst defaultOptions = {\n  timeout: 13000,                                // The timeout (in ms) for the HTTP requests.\n  cookieName: \"hanko\",                           // The cookie name under which the session token is set.\n  localStorageKey: \"hanko\",                      // The prefix / name of the localStorage keys.\n  sessionCheckInterval: 30000,                   // Interval (in ms) for session validity checks. Must be greater than 3000 (3s).\n  sessionCheckChannelName: \"hanko-session-check\" // The broadcast channel name for inter-tab communication\n\n};\nconst hanko = new Hanko(\"http://localhost:3000\", defaultOptions);\n```\n\n### Session Events\n\nYou can bind callback functions to different custom events. The callback function will be called when the event happens\nand an object will be passed in, containing event details. The event binding works as follows:\n\n```typescript\n// Controls the optional `once` parameter. When set to `true` the callback function will be called only once.\nconst once = false;\n\nconst removeEventListener = hanko.onSessionCreated((eventDetail) => {\n    // Your code...\n}, once);\n```\n\nThe following events are available:\n\n- \"hanko-session-created\": Will be triggered after a session has been created and the user has completed possible\n  additional steps (e.g. passkey registration or password recovery). It will also be triggered when the user logs in via\n  another browser window. The event can be used to obtain the JWT claims.\n\n```js\nhanko.onSessionCreated((sessionDetail) => {\n  // A new JWT has been issued.\n    console.info(\"Session created\", sessionDetail.claims);\n})\n```\n\n- \"hanko-session-expired\": Will be triggered when the session has expired, or when the session has been removed in\n  another browser window, because the user has logged out, or deleted the account.\n\n```js\nhanko.onSessionExpired(() => {\n  // You can redirect the user to a login page or show the `<hanko-auth>` element, or to prompt the user to log in again.\n  console.info(\"Session expired\");\n})\n```\n\n- \"hanko-user-logged-out\": Will be triggered, when the user actively logs out. In other browser windows, a \"hanko-session-expired\" event\n  will be triggered at the same time.\n\n```js\nhanko.onUserLoggedOut(() => {\n  // You can redirect the user to a login page or show the `<hanko-auth>` element.\n  console.info(\"User logged out\");\n})\n```\n\n- \"hanko-user-deleted\": Will be triggered when the user has deleted the account. In other browser windows, a \"hanko-session-expired\" event\n  will be triggered at the same time.\n\n```js\nhanko.onUserDeleted(() => {\n  // You can redirect the user to a login page or show the `<hanko-auth>` element.\n  console.info(\"User has been deleted\");\n})\n```\n\nPlease Take a look into the [docs](https://teamhanko.github.io/hanko/jsdoc/hanko-frontend-sdk/) for more details.\n\n### Session Management\n\nThe SDK provides methods to manage user sessions and retrieve user information.\n\n#### Getting the User Object\n\nFetches the current user's profile information.\n\n- **Returns**: A `User` object containing the user’s profile details. The object includes:\n    - `user_id`: A unique string identifier for the user.\n    - `passkeys`: An optional array of WebAuthn credentials (passkey-based authentication).\n    - `security_keys`: An optional array of WebAuthn credentials (security key-based authentication).\n    - `mfa_config`: An optional configuration object for multi-factor authentication settings.\n    - `emails`: An optional array of email objects (e.g., `{ address: string, is_primary: boolean, is_verified: boolean }`).\n    - `username`: An optional username object (e.g., `{ id: string, username: string }`).\n    - `created_at`: A string timestamp (ISO 8601) of when the user was created.\n    - `updated_at`: A string timestamp (ISO 8601) of when the user was last updated.\n- **Errors**: `UnauthorizedError` (invalid or expired session), `TechnicalError` (server or network issues).\n\n```typescript\ntry {\n    const user = await hanko.getUser();\n    console.log(\"User profile:\", user);\n    // Example output:\n    // {\n    //   user_id: \"123e4567-e89b-12d3-a456-426614174000\",\n    //   emails: [{ address: \"user@example.com\", is_primary: true, is_verified: true }],\n    //   username: { id: \"f2882293-3c39-451d-a7cb-4cf3375e0c66\", username: \"johndoe\" },\n    //   created_at: \"2025-01-01T10:00:00Z\",\n    //   updated_at: \"2025-04-01T12:00:00Z\"\n    // }\n} catch (error) {\n    console.error(\"Failed to fetch user profile:\", error);\n    // Handle UnauthorizedError or TechnicalError\n}\n```\n\n#### Validating a Session\n\nChecks the validity of the current session.\n\n- **Returns**: A SessionCheckResponse object containing:\n    - `is_valid`: A boolean indicating whether the session is valid.\n    - `claims`: An optional object with session details, including:\n        - `subject`: The user ID or session identifier.\n        - `session_id`: The unique session identifier.\n        - `expiration`: A string timestamp (ISO 8601) when the session expires.\n        - `email`: An optional object with email details (e.g., `{ address: string, is_primary: boolean, is_verified: boolean }`).\n        - `username`: An optional string with the user’s username.\n        - `issued_at`, `audience`, `issuer`: Optional metadata about the session token.\n        - Custom claims (defined by the application).\n- **Errors**: TechnicalError (server or network issues).\n\n```typescript\ntry {\n    const sessionStatus = await hanko.validateSession();\n    console.log(\"Session status:\", sessionStatus);\n    // Example output:\n    // {\n    //   is_valid: true,\n    //   claims: {\n    //     subject: \"123e4567-e89b-12d3-a456-426614174000\",\n    //     session_id: \"789abc\",\n    //     expiration: \"2025-04-25T12:00:00Z\",\n    //     email: { address: \"user@example.com\", is_primary: true, is_verified: true },\n    //     custom_field: \"value\"\n    //   }\n    // }\n} catch (error) {\n    console.error(\"Failed to validate session:\", error);\n    // Handle TechnicalError\n}\n```\n\n#### Getting the Session Token\n\nRetrieves the current session token from the authentication cookie.\n\n- **Returns**: A string containing the JWT session token or `null` if no session exists.\n- **Note**: This method does not throw errors; check for `null` to handle missing sessions.\n\n```typescript\nconst token = hanko.getSessionToken();\nconsole.log(\"Session token:\", token);\n// Example output: \"eyJhbGciOiJIUzI1NiIs...\"\n```\n\n#### Logging out a User\n\nLogs out the current user by invalidating the session.\n\n- **Returns**: A promise that resolves with no value on successful logout or throws an error.\n- **Errors**: `TechnicalError` (server or network issues).\n- **Note**: If no session exists, the method resolves without error.\n\n```typescript\ntry {\n    await hanko.logout();\n    console.log(\"User logged out\");\n} catch (error) {\n    console.error(\"Failed to fetch user logout:\", error);\n    // Handle TechnicalError\n}\n```\n\n### Translation of outgoing emails\n\nIf you use the main `Hanko` client provided by the Frontend SDK, you can use the `lang` parameter in the options when\ninstantiating the client to configure the language that is used to convey to the Hanko API the\nlanguage to use for outgoing emails. If you have disabled email delivery through Hanko and configured a webhook for the\n`email.send` event, the value for the `lang` parameter is reflected in the JWT payload of the token contained in the\nwebhook request in the \"Language\" claim.\n\n### Custom session claim type safety\n\nThe Hanko backend allows you to define custom claims that are added to issued session JWTs\n(see [here](https://github.com/teamhanko/hanko/blob/main/backend/README.md#session-jwt-templates) for more info).\n\nTo allow for IDE autocompletion and to maintain type safety for your custom claims:\n\n1. Create a TypeScript definition file (`*.d.ts`) in your project (alternatively, modify an existing one).\n2. Import the `Claims` type from the frontend SDK.\n3. Declare a custom type that extends the `Claims` type.\n4. Add your custom claims to your custom type.\n\n```ts\nimport type { Claims } from \"@teamhanko/hanko-frontend-sdk\" // 2.\n// import type { Claims } from \"@teamhanko/elements\"        // alternatively, if you use Hanko Elements, which\n                                                            // re-exports most SDK types\n\n\ntype CustomClaims = Claims<{                                // 3.\n    custom_claim?: string                                   // 4.\n}>;\n```\n\n5. Use your custom type when accessing claims, e.g. in session details received in event callbacks or when accessing\nclaims in responses from session validation\n[endpoints](https://docs.hanko.io/api-reference/public/session-management/validate-a-session):\n\n```ts\nimport type { CustomClaims } from \"...\"; // path to your type declaration file\n\nhanko.onSessionCreated((sessionDetail) => {\n  const claims = sessionDetail.claims as CustomClaims;\n  console.info(\"My custom claim:\", claims.custom_claim);\n});\n```\n\n```ts\nimport type { CustomClaims } from \"...\"; // path to your type declaration file\n\nasync function session() {\n    const session = await hanko.validateSession();\n    const claims = session.claims as CustomClaims;\n    console.info(\"My custom claim:\", claims.custom_claim);\n};\n```\n\n## FlowAPI\n\nThe SDK offers a TypeScript-based interface for managing authentication and profile flows with Hanko, enabling the\ndevelopment of custom frontends with the Hanko FlowAPI. It handles state transitions, action execution, input\nvalidation, and event dispatching, while also providing built-in support for auto-stepping and passkey autofill.\nThis guide explores its core functionality and usage patterns.\n\n### Initializing a New Flow\n\nStart a new authentication or profile flow using the `createState` method on a Hanko instance. Options allow you to control\nevent dispatching and auto-step behavior.\n\n```typescript\nconst state = await hanko.createState(\"login\", {\n    dispatchAfterStateChangeEvent: true,\n    excludeAutoSteps: [],\n    loadFromCache: true,\n    cacheKey: \"hanko-flow-state\",\n});\n```\n\n#### Parameters\n\n- **flowName**: The name of the flow (e.g., \"login\", \"register\" or \"profile\").\n- **options**:\n    - **dispatchAfterStateChangeEvent**: `boolean` - Whether to dispatch the onAfterStateChange event after state changes (default: true).\n    - **excludeAutoSteps**: `AutoStepExclusion` - Array of state names or \"all\" to skip specific or all auto-steps (default: null).\n    - **loadFromCache**: `boolean` - Whether to attempt loading a cached state from localStorage (default: true).\n    - **cacheKey**: `string` - The key used for localStorage caching (default: \"hanko-flow-state\").\n\n\n### Understanding the State Object\n\nThe `state` object represents the current step in the flow. It contains properties and methods to interact with the flow.\n\n#### Structure\n\n- **name**: `StateName` - The current state’s name (e.g., \"login_init\", \"login_password\", \"success\").\n- **flowName**: `FlowName` - The name of the flow (e.g., \"login\").\n- **error**: `Error | undefined` - An error object if an action or request fails (e.g., invalid input, network error).\n- **payload**: `Payloads[StateName] | undefined` - State-specific data returned by the API.\n- **actions**: `ActionMap<StateName>` - An object mapping action names to Action instances.\n- **csrfToken**: `string` - CSRF token for secure requests.\n- **status**: `number` - HTTP status code of the last response.\n- **invokedAction**: `ActionInfo | undefined` - Details of the last action run on this state, if any.\n- **previousAction**: `ActionInfo | undefined` - Details of the action that led to this state, if any.\n- **isCached**: `boolean` - Whether the state was loaded from localStorage.\n- **cacheKey**: `string` - The key used for localStorage caching.\n- **excludeAutoSteps**: `AutoStepExclusion` - An array of `StateNames` excluded from auto-stepping.\n\n\n### Action Availability\n\nActions can be enabled or disabled based on the backend configuration or the user's state and properties. You can check\nwhether a specific action is enabled by accessing its `enabled` property:\n\n```typescript\nif (state.actions.example_action.enabled) {\n  await state.actions.example_action.run();\n} else {\n  console.log(\"Action is disabled\");\n}\n```\n\n### Accessing Action Inputs\n\nEach action in `state.actions` has an `inputs` property defining expected input fields.\n\n```typescript\nconsole.log(state.actions.continue_with_login_identifier.inputs);\n// Example output:\n// {\n//   username: {\n//     required: true,\n//     type: \"string\",\n//     minLength: 3,\n//     maxLength: 20,\n//     description: \"User’s login name\"\n//   }\n// }\n```\n\n### Running an Action\n\nActions transition the flow to a new state. Use the `run` method on an action, passing input values and optional configuration.\n\n#### Basic Example with Type Narrowing\n\n```typescript\nif (state.name === \"login_init\") {\n  const newState = await state.actions.continue_with_login_identifier.run({\n    username: \"user1\",\n  });\n  // Triggers `onBeforeStateChange` and `onAfterStateChange` events\n  // `newState` is the next state in the flow (e.g., \"login_password\")\n}\n```\n\n#### Additional Considerations\n\n- **Type Narrowing**: Check `state.name` to ensure the action exists and inputs are valid for that state.\n- **Events**: By default, `run` triggers `onBeforeStateChange` before the action and `onAfterStateChange` after the new state is loaded.\n- **Validation Errors**: If the action fails due to invalid input (e.g., wrong format or length), `newState.error` will be set to \"invalid_form_data\", and specific errors will be attached to the related input fields (see \"Error Handling\" below).\n\n### Event Handlers\n\nThe SDK dispatches events via the Hanko instance to track state changes.\n\n#### `onBeforeStateChange`\n\nFires before an action is executed, useful for showing loading states.\n\n```typescript\nhanko.onBeforeStateChange(({ state }) => {\n  console.log(\"Action loading:\", state.invokedAction);\n});\n```\n\n#### `onAfterStateChange`\n\nFires after a new state is loaded, ideal for rendering UI or handling state-specific logic.\n\n```typescript\nhanko.onAfterStateChange(({ state }) => {\n  console.log(\"Action load finished:\", state.invokedAction);\n\n  switch (state.name) {\n    case \"login_init\":\n      state.passkeyAutofillActivation(); // Special handler for passkey autofill; requires an <input> field on the page with `autocomplete=\"username webauthn\"` (e.g., <input type=\"text\" name=\"username\" autocomplete=\"username webauthn\" />) so the browser can suggest and autofill passkeys when the user interacts with it.\n      break;\n    case \"login_password\":\n      // Render password input UI\n      if (state.error) {\n        console.log(\"Error:\", state.error); // e.g., \"invalid_form_data\"\n      }\n      break;\n    case \"error\":\n      // Handle network errors or 5xx responses\n      console.error(\"Flow error:\", state.error);\n      break;\n  }\n});\n```\n\n### Controlling the AfterStateChanged Event\n\nYou can disable the automatic `onAfterStateChange` event and dispatch it manually after custom logic.\n\n```typescript\nif (state.name === \"login_init\") {\n  const newState = await state.actions.continue_with_login_identifier.run(\n    { username: \"user1\" },\n    { dispatchAfterStateChangeEvent: false }, // Disable automatic dispatch\n  );\n  // Only `onBeforeStateChange` is triggered here\n\n  await doSomething(); // Your custom async logic\n  newState.dispatchAfterStateChangeEvent(); // Manually trigger the event\n}\n```\n\n### Auto-Steps\n\nAuto-steps automatically advance the flow for certain states, reducing manual intervention.\n\n#### Supported States\n\n- `preflight`\n- `login_passkey`\n- `onboarding_verify_passkey_attestation`\n- `webauthn_credential_verification`\n- `thirdparty`\n- `success`\n- `account_deleted`\n\n#### Disabling Auto-Steps\n\nPrevent auto-steps by specifying states in `excludeAutoSteps`:\n\n```typescript\nconst state = await hanko.createState(\"login\", {\n  excludeAutoSteps: [\"success\"], // Skip auto-step for \"success\"\n});\n```\n\n#### Manual Auto-Step Execution\n\n```typescript\nhanko.onAfterStateChange(({ state }) => {\n  if (state.name === \"success\") {\n    console.log(\"Flow completed\");\n    await state.autoStep();\n  }\n});\n```\n\n### Error Handling\n\n#### Input Errors\n\nIf an action fails due to invalid inputs:\n\n```typescript\nif (state.name === \"login_password\" && state.error === \"invalid_form_data\") {\n  const passwordError = state.actions.password_login.inputs.password.error;\n  console.log(\"Password error:\", passwordError);\n}\n```\n\n#### Network/API Errors\n\nFor network issues or `5xx` responses, the `error` state is entered with details in `state.error`.\n\n```typescript\nif (state.name === \"error\") {\n    console.error(\"Flow error:\", state.error);\n}\n```\n\n### Caching Flow State\n\nPersist and recover flow state using `localStorage`.\n\n#### Saving State\n\nSave the current flow state to `localStorage` using `saveToLocalStorage()`.\n\n```typescript\nstate.saveToLocalStorage(); // Stores under `state.cacheKey` (default: \"hanko-flow-state\")\n```\n\nPlease note that the `localStorage` entry will be removed automatically when an action is invoked on the saved state.\n\n#### Loading State\n\nRecover a cached state or start a new flow:\n\n```typescript\nconst state = await hanko.createState(\"login\", {\n    loadFromCache: true, // Attempts to load from `cacheKey`\n    cacheKey: \"hanko-flow-state\",\n});\n```\n\n#### Clearing State\n\nRemove the cached state:\n\n```typescript\nstate.removeFromLocalStorage(); // Deletes from `state.cacheKey`\n```\n\n#### Advanced Serialization\n\nFor custom persistence:\n\n```typescript\nimport { State } from \"@teamhanko/hanko-frontend-sdk\";\n\nconst serialized = state.serialize(); // Returns a `SerializedState` object\n// Store `serialized` in your storage system\n\n// Later, deserialize it\nconst recoveredState = await State.deserialize(hanko, serialized, {\n    cacheKey: \"custom-key\",\n});\n```\n\nThis allows integration with other storage mechanisms.\n\n\n## Bugs\n\nFound a bug? Please report on our [GitHub](https://github.com/teamhanko/hanko/issues) page.\n\n## Documentation\n\nTo see the latest documentation, please click [here](https://teamhanko.github.io/hanko/jsdoc/hanko-frontend-sdk/).\n\n## License\n\nThe `hanko-frontend-sdk` project is licensed under the [MIT License](LICENSE).\n"
  },
  {
    "path": "frontend/frontend-sdk/jest.config.cjs",
    "content": "/** @type {import('ts-jest').JestConfigWithTsJest} */\nmodule.exports = {\n  preset: 'ts-jest',\n  testEnvironment: 'jsdom',\n  setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],\n  coverageProvider: \"v8\",\n};\n"
  },
  {
    "path": "frontend/frontend-sdk/jsdoc.json",
    "content": "{\n  \"opts\": {\n    \"template\": \"node_modules/better-docs\"\n  },\n  \"tags\": {\n    \"allowUnknownTags\": [\"category\", \"subcategory\"],\n    \"dictionaries\": [\"jsdoc\", \"closure\"]\n  },\n  \"plugins\": [\n    \"node_modules/better-docs/typescript\",\n    \"node_modules/better-docs/category\"\n  ],\n  \"source\": {\n    \"include\": [\"./src\"],\n    \"includePattern\": \".(ts)$\"\n  },\n  \"templates\": {\n    \"cleverLinks\": true\n  },\n  \"sourceType\": \"module\"\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/nginx/default.conf",
    "content": "server {\n  listen       80;\n  server_name  localhost;\n\n  server_tokens off;\n\n  location / {\n    root   /usr/share/nginx/html;\n#     index  index.html;\n    try_files $uri $uri/ @index;\n  }\n\n  location @index {\n    # add_header Cache-Control no-cache;\n    server_tokens off;\n    expires -1;\n    root   /usr/share/nginx/html;\n    try_files /index.html =404;\n  }\n\n  # redirect server error pages to the static page /50x.html\n  #\n  error_page   500 502 503 504  /50x.html;\n  location = /50x.html {\n    root   /usr/share/nginx/html;\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/package.json",
    "content": "{\n  \"name\": \"@teamhanko/hanko-frontend-sdk\",\n  \"version\": \"2.5.0\",\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"type\": \"module\",\n  \"source\": \"src/index.ts\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"require\": \"./dist/sdk.cjs\",\n      \"types\": \"./dist/index.d.ts\",\n      \"default\": \"./dist/sdk.modern.js\"\n    }\n  },\n  \"main\": \"./dist/sdk.cjs\",\n  \"module\": \"./dist/sdk.module.js\",\n  \"unpkg\": \"./dist/sdk.umd.js\",\n  \"scripts\": {\n    \"lint\": \"eslint 'src/**/*.ts'\",\n    \"format\": \"pretty-quick --staged\",\n    \"build\": \"microbundle --globals @github/webauthn-json=webauthnJson --tsconfig tsconfig.prod.json\",\n    \"dev\": \"microbundle watch\",\n    \"docs\": \"jsdoc -r -c jsdoc.json -d ./.generated/docs -R README.md --pedantic\",\n    \"test\": \"jest --coverage\"\n  },\n  \"description\": \"A package for simplifying UI integration with the Hanko API. It is meant for use in browsers only.\",\n  \"repository\": \"github:teamhanko/hanko\",\n  \"author\": \"Hanko GmbH <developers@hanko.io>\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"hanko\",\n    \"passkey\",\n    \"webauthn\",\n    \"passcode\",\n    \"password\",\n    \"frontend\",\n    \"client\",\n    \"sdk\"\n  ],\n  \"homepage\": \"https://hanko.io\",\n  \"devDependencies\": {\n    \"@github/webauthn-json\": \"^2.1.1\",\n    \"@types/jest\": \"^30.0.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.54.0\",\n    \"better-docs\": \"^2.7.2\",\n    \"eslint\": \"^8.52.0\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"eslint-config-preact\": \"^1.3.0\",\n    \"eslint-config-prettier\": \"^10.1.8\",\n    \"eslint-plugin-prettier\": \"^5.5.5\",\n    \"eslint-plugin-promise\": \"^6.1.1\",\n    \"jest\": \"^30.2.0\",\n    \"jest-environment-jsdom\": \"^29.7.0\",\n    \"js-cookie\": \"^3.0.5\",\n    \"jsdoc\": \"3.6.11\",\n    \"microbundle\": \"^0.15.1\",\n    \"ts-jest\": \"^29.1.1\",\n    \"ts-loader\": \"^9.4.2\",\n    \"typescript\": \"^4.9.5\"\n  },\n  \"dependencies\": {\n    \"@types/js-cookie\": \"^3.0.3\"\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/Hanko.ts",
    "content": "import { Listener } from \"./lib/events/Listener\";\nimport { Relay } from \"./lib/events/Relay\";\nimport { Cookie, CookieSameSite } from \"./lib/Cookie\";\nimport { SessionClient } from \"./lib/client/SessionClient\";\nimport { HttpClient, SessionTokenLocation } from \"./lib/client/HttpClient\";\nimport { FlowName } from \"./lib/flow-api/types/flow\";\nimport { StateCreateConfig, State } from \"./lib/flow-api/State\";\nimport { UserClient } from \"./lib/client/UserClient\";\nimport { SessionCheckResponse } from \"./lib/Dto\";\nimport { User } from \"./lib/flow-api/types/payload\";\n\n/**\n * The options for the Hanko class\n *\n * @interface\n * @property {number=} timeout - The http request timeout in milliseconds. Defaults to 13000ms\n * @property {string=} cookieName - The name of the session cookie set from the SDK. Defaults to \"hanko\"\n * @property {string=} cookieDomain - The domain where the cookie set from the SDK is available. Defaults to the domain of the page where the cookie was created.\n * @property {string=} cookieSameSite - Specify whether/when cookies are sent with cross-site requests. Defaults to \"lax\".\n * @property {string=} localStorageKey - The prefix / name of the local storage keys. Defaults to \"hanko\"\n * @property {string=} lang - Used to convey the preferred language to the API, e.g. for translating outgoing emails.\n *                            It is transmitted to the API in a custom header (X-Language).\n *                            Should match one of the supported languages (\"bn\", \"de\", \"en\", \"fr\", \"it, \"nl\", \"pt-BR\", \"zh\")\n *                            if email delivery by Hanko is enabled. If email delivery by Hanko is disabled and the\n *                            relying party configures a webhook for the \"email.send\" event, then the set language is\n *                            reflected in the payload of the token contained in the webhook request.\n * @property {number=} sessionCheckInterval -  Interval for session validity checks in milliseconds. Must be greater than 3000 (3s), defaults to 3000 otherwise.\n * @property {string=} sessionCheckChannelName - The broadcast channel name for inter-tab communication.\n * @property {string=} sessionTokenLocation - The location where the session token is stored.\n */\nexport interface HankoOptions {\n  timeout?: number;\n  cookieName?: string;\n  cookieDomain?: string;\n  cookieSameSite?: CookieSameSite;\n  localStorageKey?: string;\n  lang?: string;\n  sessionCheckInterval?: number;\n  sessionCheckChannelName?: string;\n  sessionTokenLocation?: SessionTokenLocation;\n}\n\n/**\n * A class that bundles all available SDK functions.\n *\n * @extends {Listener}\n * @param {string} api - The URL of your Hanko API instance\n * @param {HankoOptions=} options - The options that can be used\n */\nclass Hanko extends Listener {\n  private readonly session: SessionClient;\n  private readonly user: UserClient;\n  private readonly cookie: Cookie;\n  public readonly client: HttpClient;\n  public readonly relay: Relay;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(api: string, options?: HankoOptions) {\n    super();\n    const opts: HankoOptions = {\n      timeout: 13000,\n      cookieName: \"hanko\",\n      localStorageKey: \"hanko\",\n      sessionCheckInterval: 30000,\n      sessionCheckChannelName: \"hanko-session-check\",\n      ...options,\n    };\n\n    /**\n     *  @public\n     *  @type {Client}\n     */\n    this.client = new HttpClient(api, opts);\n    /**\n     *  @public\n     *  @type {SessionClient}\n     */\n    this.session = new SessionClient(api, opts);\n    /**\n     *  @public\n     *  @type {SessionClient}\n     */\n    this.user = new UserClient(api, opts);\n    /**\n     *  @public\n     *  @type {Relay}\n     */\n    this.relay = new Relay(api, opts);\n    /**\n     *  @public\n     *  @type {Cookie}\n     */\n    this.cookie = new Cookie(opts);\n  }\n\n  /**\n   * Sets the preferred language on the underlying sub-clients. The clients'\n   * base HttpClient uses this language to transmit an X-Language header to the\n   * API which is then used to e.g. translate outgoing emails.\n   *\n   * @public\n   * @param lang {string} - The preferred language to convey to the API.\n   */\n  setLang(lang: string) {\n    this.client.lang = lang;\n  }\n\n  /**\n   * Creates a new flow state for the specified flow.\n   *\n   * This method initializes a state by either loading from cache (if configured) or fetching from the server.\n   * It uses the provided configuration to control caching, event dispatching, and auto-step behavior.\n   *\n   * @param {FlowName} flowName - The name of the flow to create a state for.\n   * @param {StateCreateConfig} [config={}] - Configuration options for state creation.\n   * @param {boolean} [config.dispatchAfterStateChangeEvent=true] - Whether to dispatch an event after the state changes.\n   * @param {AutoStepExclusion} [config.excludeAutoSteps=null] - States to exclude from auto-step processing, or `\"all\"` to skip all auto-steps.\n   * @param {string} [config.cacheKey=\"hanko-flow-state\"] - Key used for caching the state in localStorage.\n   * @param {boolean} [config.loadFromCache=true] - Whether to attempt loading the state from cache.\n   * @returns {Promise<AnyState>} A promise that resolves to the created flow state.\n   * @category SDK\n   * @subcategory FlowAPI\n   */\n  createState(flowName: FlowName, config: StateCreateConfig = {}) {\n    return State.create(this, flowName, config);\n  }\n\n  /**\n   * Retrieves the current user's profile information.\n   *\n   * @public\n   * @deprecated\n   * @returns {Promise<User>} A promise that resolves to the user object\n   * @throws {UnauthorizedError} If the user is not authenticated\n   * @throws {TechnicalError} If an unexpected error occurs\n   */\n  async getUser(): Promise<User> {\n    return this.user.getCurrent();\n  }\n\n  /**\n   * Retrieves the current user's profile information.\n   *\n   * @public\n   * @returns {Promise<User>} A promise that resolves to the user object\n   * @throws {UnauthorizedError} If the user is not authenticated\n   * @throws {TechnicalError} If an unexpected error occurs\n   */\n  async getCurrentUser(): Promise<User> {\n    return this.user.getCurrentUser();\n  }\n\n  /**\n   * Validates the current session.\n   *\n   * @public\n   * @returns {Promise<SessionCheckResponse>} A promise that resolves to the session check response\n   */\n  async validateSession(): Promise<SessionCheckResponse> {\n    return this.session.validate();\n  }\n\n  /**\n   * Retrieves the current session token from the authentication cookie.\n   *\n   * @public\n   * @returns {string} The session token\n   */\n  getSessionToken(): string {\n    return this.cookie.getAuthCookie();\n  }\n\n  /**\n   * Logs out the current user by invalidating the session.\n   *\n   * @public\n   * @returns {Promise<void>} A promise that resolves when the logout is complete\n   */\n  async logout(): Promise<void> {\n    return this.user.logout();\n  }\n}\n\nexport { Hanko };\n"
  },
  {
    "path": "frontend/frontend-sdk/src/declarations.d.ts",
    "content": "import {\n  CustomEventWithDetail,\n  SessionDetail,\n  sessionCreatedType,\n  sessionExpiredType,\n  userLoggedOutType,\n  userDeletedType,\n  flowErrorType,\n} from \"./lib/events/CustomEvents\";\n\ndeclare global {\n  // eslint-disable-next-line no-unused-vars\n  interface DocumentEventMap {\n    [sessionCreatedType]: CustomEventWithDetail<SessionDetail>;\n    [sessionExpiredType]: CustomEventWithDetail<null>;\n    [userLoggedOutType]: CustomEventWithDetail<null>;\n    [userDeletedType]: CustomEventWithDetail<null>;\n    [flowErrorType]: CustomEventWithDetail<Error>;\n  }\n}\n\nexport {};\n"
  },
  {
    "path": "frontend/frontend-sdk/src/index.ts",
    "content": "// SDK\n\nimport { Hanko } from \"./Hanko\";\n\nexport { Hanko };\n\n// Clients\n\nimport { HttpClient } from \"./lib/client/HttpClient\";\nimport { Client } from \"./lib/client/Client\";\nimport { SessionClient } from \"./lib/client/SessionClient\";\nimport { UserClient } from \"./lib/client/UserClient\";\n\nexport { HttpClient, Client, SessionClient, UserClient };\n\n// Events\n\nimport { Relay } from \"./lib/events/Relay\";\n\nexport { Relay };\n\n// Utils\n\nimport { WebauthnSupport } from \"./lib/WebauthnSupport\";\nimport {\n  generateCodeVerifier,\n  setStoredCodeVerifier,\n  getStoredCodeVerifier,\n  clearStoredCodeVerifier,\n} from \"./lib/Pkce\";\n\nexport {\n  WebauthnSupport,\n  generateCodeVerifier,\n  setStoredCodeVerifier,\n  getStoredCodeVerifier,\n  clearStoredCodeVerifier,\n};\n\n// DTO\n\nimport {\n  Email,\n  Emails,\n  Identity,\n  SessionCheckResponse,\n  Claims,\n} from \"./lib/Dto\";\n\nexport type { Email, Emails, Identity, SessionCheckResponse, Claims };\n\n// Errors\n\nimport {\n  HankoError,\n  ConflictError,\n  ForbiddenError,\n  EmailAddressAlreadyExistsError,\n  InvalidPasswordError,\n  InvalidPasscodeError,\n  InvalidWebauthnCredentialError,\n  MaxNumOfEmailAddressesReachedError,\n  MaxNumOfPasscodeAttemptsReachedError,\n  NotFoundError,\n  PasscodeExpiredError,\n  RequestTimeoutError,\n  TechnicalError,\n  ThirdPartyError,\n  TooManyRequestsError,\n  UnauthorizedError,\n  UserVerificationError,\n  WebauthnRequestCancelledError,\n} from \"./lib/Errors\";\n\nexport {\n  HankoError,\n  ConflictError,\n  ForbiddenError,\n  EmailAddressAlreadyExistsError,\n  InvalidPasswordError,\n  InvalidPasscodeError,\n  InvalidWebauthnCredentialError,\n  MaxNumOfEmailAddressesReachedError,\n  MaxNumOfPasscodeAttemptsReachedError,\n  NotFoundError,\n  PasscodeExpiredError,\n  RequestTimeoutError,\n  TechnicalError,\n  ThirdPartyError,\n  TooManyRequestsError,\n  UnauthorizedError,\n  UserVerificationError,\n  WebauthnRequestCancelledError,\n};\n\n// Events\n\nimport {\n  CustomEventWithDetail,\n  SessionDetail,\n  FlowDetail,\n  sessionCreatedType,\n  sessionExpiredType,\n  userLoggedOutType,\n  userDeletedType,\n} from \"./lib/events/CustomEvents\";\n\nexport type { SessionDetail };\nexport type { FlowDetail };\n\nexport {\n  sessionCreatedType,\n  sessionExpiredType,\n  userLoggedOutType,\n  userDeletedType,\n  CustomEventWithDetail,\n};\n\n// Misc\n\nimport { CookieSameSite } from \"./lib/Cookie\";\n\nexport type { CookieSameSite };\n\n// Flow\nexport * from \"./lib/flow-api/State\";\nexport * from \"./lib/flow-api/types/flow\";\nexport * from \"./lib/flow-api/types/flowError\";\nexport * from \"./lib/flow-api/types/payload\";\nexport * from \"./lib/flow-api/types/state\";\nexport * from \"./lib/flow-api/types/input\";\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/Cookie.ts",
    "content": "import JSCookie, { CookieAttributes } from \"js-cookie\";\nimport { TechnicalError } from \"./Errors\";\n\n/**\n * Options for Cookie\n *\n * @category SDK\n * @subcategory Internal\n * @property {string=} cookieName - The name of the session cookie set from the SDK. Defaults to \"hanko\".\n * @property {string=} cookieDomain - The domain where the cookie set from the SDK is available. Defaults to the domain of the page where the cookie was created.\n * @property {string=} cookieSameSite -Specify whether/when cookies are sent with cross-site requests. Defaults to \"lax\".\n */\ninterface CookieOptions {\n  cookieName?: string;\n  cookieDomain?: string;\n  cookieSameSite?: CookieSameSite;\n}\n\nexport type CookieSameSite =\n  | \"strict\"\n  | \"Strict\"\n  | \"lax\"\n  | \"Lax\"\n  | \"none\"\n  | \"None\";\n\n/**\n * A class to manage cookies.\n *\n * @category SDK\n * @subcategory Internal\n * @param {CookieOptions} options - The options that can be used\n */\nexport class Cookie {\n  authCookieName: string;\n  authCookieDomain?: string;\n  authCookieSameSite: CookieSameSite;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(options: CookieOptions) {\n    this.authCookieName = options.cookieName ?? \"hanko\";\n    this.authCookieDomain = options.cookieDomain;\n    this.authCookieSameSite = options.cookieSameSite ?? \"lax\";\n  }\n\n  /**\n   * Returns the authentication token that was stored in the cookie.\n   *\n   * @return {string}\n   */\n  getAuthCookie(): string {\n    return JSCookie.get(this.authCookieName);\n  }\n\n  /**\n   * Stores the authentication token to the cookie.\n   *\n   * @param {string} token - The authentication token to be stored.\n   * @param {CookieAttributes} options - Options for setting the auth cookie.\n   */\n  setAuthCookie(token: string, options?: CookieAttributes) {\n    const defaults: CookieAttributes = {\n      secure: true,\n      sameSite: this.authCookieSameSite,\n    };\n\n    if (this.authCookieDomain !== undefined) {\n      defaults.domain = this.authCookieDomain;\n    }\n\n    const o: CookieAttributes = { ...defaults, ...options };\n\n    if (\n      (o.sameSite === \"none\" || o.sameSite === \"None\") &&\n      o.secure === false\n    ) {\n      throw new TechnicalError(\n        new Error(\"Secure attribute must be set when SameSite=None\"),\n      );\n    }\n\n    JSCookie.set(this.authCookieName, token, o);\n  }\n\n  /**\n   * Removes the cookie used for authentication.\n   */\n  removeAuthCookie() {\n    JSCookie.remove(this.authCookieName);\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/Dto.ts",
    "content": "/**\n * @interface\n * @category SDK\n * @subcategory DTO\n * @property {string} id - The UUID of the current user.\n * @ignore\n */\nexport interface Me {\n  id: string;\n}\n\n/**\n * @interface\n * @category SDK\n * @subcategory DTO\n * @property {string} id - The UUID of the email address.\n * @property {string} address - The email address.\n * @property {boolean} is_verified - Indicates whether the email address is verified.\n * @property {boolean} is_primary - Indicates it's the primary email address.\n * @property {Identity} identity - Indicates that this email is linked to a third party account.\n * @property {Identity[]} identities - A list of identities, each identity indicates that this email is linked to a third party account.\n */\nexport interface Email {\n  id: string;\n  address: string;\n  is_verified: boolean;\n  is_primary: boolean;\n  identity?: Identity;\n  identities?: Identity[];\n}\n\n/**\n * @interface\n * @category SDK\n * @subcategory DTO\n * @property {Email[]} - A list of emails assigned to the current user.\n */\nexport interface Emails extends Array<Email> {}\n\n/**\n * @interface\n * @category SDK\n * @subcategory DTO\n * @property {string} id - The subject ID with the third party provider.\n * @property {string} provider - The third party provider name.\n */\nexport interface Identity {\n  id: string;\n  provider: string;\n  readonly identity_id?: string;\n}\n\n/**\n * Represents the claims associated with a session or token. Includes standard claims such as `subject`, `issued_at`,\n * `expiration`, and others, as well as custom claims defined by the user.\n *\n * @template TCustomClaims - An optional generic parameter that represents custom claims.\n *                           It extends a record with string keys and unknown values.\n *                           Defaults to `Record<string, unknown>` if not provided.\n *\n * @interface\n * @category SDK\n * @subcategory DTO\n * @property {string} subject - The subject or identifier of the claims.\n * @property {string} [issued_at] - The timestamp when the claims were issued (optional).\n * @property {string} expiration - The timestamp when the claims expire.\n * @property {string[]} [audience] - The intended audience(s) for the claims (optional).\n * @property {string} [issuer] - The entity that issued the claims (optional).\n * @property {Pick<Email, \"address\" | \"is_primary\" | \"is_verified\">} [email] - Email information associated with the subject (optional).\n * @property {string} [username] - The subject's username (optional).\n * @property {string} session_id - The session identifier linked to the claims.\n *\n * @description Custom claims can be added via the `TCustomClaims` generic parameter, which will be merged\n * with the standard claims properties. These custom claims must follow the `Record<string, unknown>` pattern.\n */\nexport type Claims<\n  TCustomClaims extends Record<string, unknown> = Record<string, unknown>,\n> = {\n  subject: string;\n  issued_at?: string;\n  expiration: string;\n  audience?: string[];\n  issuer?: string;\n  email?: Pick<Email, \"address\" | \"is_primary\" | \"is_verified\">;\n  username?: string;\n  session_id: string;\n} & TCustomClaims;\n\n/**\n * Represents the response from a session validation or retrieval operation.\n *\n * @interface\n * @category SDK\n * @subcategory DTO\n * @property {boolean} is_valid - Indicates whether the session is valid.\n * @property {Claims} [claims] - The claims associated with the session (optional).\n * @property {string} [expiration_time] - The expiration timestamp of the session (optional).\n * @property {string} [user_id] - The user ID linked to the session (optional).\n */\nexport interface SessionCheckResponse {\n  is_valid: boolean;\n  claims?: Claims;\n  expiration_time?: string;\n  user_id?: string;\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/Errors.ts",
    "content": "/**\n * Every error thrown in the SDK is an instance of 'HankoError'. The value of the 'code' property is eligible to\n * translate the error into an error message.\n *\n * @extends {Error}\n * @category SDK\n * @subcategory Errors\n * @param code {string} - An error code that refers to the error instance.\n * @param cause {Error=} - The original error\n */\nabstract class HankoError extends Error {\n  code: string;\n  cause?: Error;\n\n  // eslint-disable-next-line require-jsdoc\n  protected constructor(message: string, code: string, cause?: Error) {\n    super(message);\n    /**\n     * @public\n     * @type {string}\n     */\n    this.code = code;\n    /**\n     * @public\n     * @type {Error=}\n     */\n    this.cause = cause;\n    Object.setPrototypeOf(this, HankoError.prototype);\n  }\n}\n\n/**\n * Every error that doesn't need to be handled in a special way is a 'TechnicalError'. Whenever you catch one, there is\n * usually nothing you can do but present an error to the user, e.g. \"Something went wrong\".\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass TechnicalError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Technical error\", \"somethingWentWrong\", cause);\n    Object.setPrototypeOf(this, TechnicalError.prototype);\n  }\n}\n\n/**\n * Attempting to create a resource that already exists results in a 'ConflictError'.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass ConflictError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(userID?: string, cause?: Error) {\n    super(\"Conflict error\", \"conflict\", cause);\n    Object.setPrototypeOf(this, ConflictError.prototype);\n  }\n}\n\n/**\n * A 'RequestTimeoutError' occurs when the specified timeout has been reached.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass RequestTimeoutError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Request timed out error\", \"requestTimeout\", cause);\n    Object.setPrototypeOf(this, RequestTimeoutError.prototype);\n  }\n}\n\n/**\n * A 'WebauthnRequestCancelledError' occurs during WebAuthn authentication or registration, when the WebAuthn API throws\n * an error. In most cases, this happens when the user cancels the browser's WebAuthn dialog.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass WebauthnRequestCancelledError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Request cancelled error\", \"requestCancelled\", cause);\n    Object.setPrototypeOf(this, WebauthnRequestCancelledError.prototype);\n  }\n}\n\n/**\n * An 'InvalidPasswordError' occurs when invalid credentials are provided when logging in with a password.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass InvalidPasswordError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Invalid password error\", \"invalidPassword\", cause);\n    Object.setPrototypeOf(this, InvalidPasswordError.prototype);\n  }\n}\n\n/**\n * An 'InvalidPasswordError' occurs when an incorrect code is entered when logging in with a passcode.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass InvalidPasscodeError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Invalid Passcode error\", \"invalidPasscode\", cause);\n    Object.setPrototypeOf(this, InvalidPasscodeError.prototype);\n  }\n}\n\n/**\n * An 'InvalidWebauthnCredentialError' occurs if invalid credentials were used when logging in with WebAuthn.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass InvalidWebauthnCredentialError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\n      \"Invalid WebAuthn credential error\",\n      \"invalidWebauthnCredential\",\n      cause,\n    );\n    Object.setPrototypeOf(this, InvalidWebauthnCredentialError.prototype);\n  }\n}\n\n/**\n * A 'PasscodeExpiredError' occurs when the passcode has expired.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass PasscodeExpiredError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Passcode expired error\", \"passcodeExpired\", cause);\n    Object.setPrototypeOf(this, PasscodeExpiredError.prototype);\n  }\n}\n\n/**\n * A 'MaxNumOfPasscodeAttemptsReachedError' occurs when an incorrect passcode is provided too many times.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass MaxNumOfPasscodeAttemptsReachedError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\n      \"Maximum number of Passcode attempts reached error\",\n      \"passcodeAttemptsReached\",\n      cause,\n    );\n    Object.setPrototypeOf(this, MaxNumOfPasscodeAttemptsReachedError.prototype);\n  }\n}\n\n/**\n * A 'NotFoundError' occurs when the requested resource was not found.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass NotFoundError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Not found error\", \"notFound\", cause);\n    Object.setPrototypeOf(this, NotFoundError.prototype);\n  }\n}\n\n/**\n * A 'TooManyRequestsError' occurs due to rate limiting when too many requests are made.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass TooManyRequestsError extends HankoError {\n  retryAfter?: number;\n  // eslint-disable-next-line require-jsdoc\n  constructor(retryAfter?: number, cause?: Error) {\n    super(\"Too many requests error\", \"tooManyRequests\", cause);\n    this.retryAfter = retryAfter;\n    Object.setPrototypeOf(this, TooManyRequestsError.prototype);\n  }\n}\n\n/**\n * An 'UnauthorizedError' occurs when the user is not authorized to access the resource.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass UnauthorizedError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Unauthorized error\", \"unauthorized\", cause);\n    Object.setPrototypeOf(this, UnauthorizedError.prototype);\n  }\n}\n\n/**\n * A 'ForbiddenError' occurs when the user is not allowed to perform the requested action.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass ForbiddenError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"Forbidden error\", \"forbidden\", cause);\n    Object.setPrototypeOf(this, ForbiddenError.prototype);\n  }\n}\n\n/**\n * A 'UserVerificationError' occurs when the user verification requirements\n * for a WebAuthn ceremony are not met.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass UserVerificationError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\"User verification error\", \"userVerification\", cause);\n    Object.setPrototypeOf(this, UserVerificationError.prototype);\n  }\n}\n\n/**\n * A 'MaxNumOfEmailAddressesReachedError' occurs when the user tries to add a new email address while the maximum number\n * of email addresses (see backend configuration) equals the number of email addresses already registered.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass MaxNumOfEmailAddressesReachedError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\n      \"Maximum number of email addresses reached error\",\n      \"maxNumOfEmailAddressesReached\",\n      cause,\n    );\n    Object.setPrototypeOf(this, MaxNumOfEmailAddressesReachedError.prototype);\n  }\n}\n\n/**\n * An 'EmailAddressAlreadyExistsError' occurs when the user tries to add a new email address which already exists.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass EmailAddressAlreadyExistsError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(cause?: Error) {\n    super(\n      \"The email address already exists\",\n      \"emailAddressAlreadyExistsError\",\n      cause,\n    );\n    Object.setPrototypeOf(this, EmailAddressAlreadyExistsError.prototype);\n  }\n}\n\n/**\n * A `ThirdPartyError` may occur during a sign in/sign up with a third party\n * provider.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass ThirdPartyError extends HankoError {\n  // eslint-disable-next-line require-jsdoc\n  constructor(code: string, cause?: Error) {\n    super(\"An error occurred during third party sign up/sign in\", code, cause);\n    Object.setPrototypeOf(this, ThirdPartyError.prototype);\n  }\n}\n\nexport {\n  HankoError,\n  TechnicalError,\n  ConflictError,\n  RequestTimeoutError,\n  WebauthnRequestCancelledError,\n  InvalidPasswordError,\n  InvalidPasscodeError,\n  InvalidWebauthnCredentialError,\n  PasscodeExpiredError,\n  MaxNumOfPasscodeAttemptsReachedError,\n  NotFoundError,\n  TooManyRequestsError,\n  UnauthorizedError,\n  ForbiddenError,\n  UserVerificationError,\n  MaxNumOfEmailAddressesReachedError,\n  EmailAddressAlreadyExistsError,\n  ThirdPartyError,\n};\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/Pkce.ts",
    "content": "const PKCE_STORAGE_KEY = \"hanko_pkce_code_verifier\";\n\n/**\n * Generates a high-entropy, cryptographically strong random string for use as a PKCE code verifier.\n * It uses rejection sampling to eliminate modulo bias, ensuring a perfectly uniform distribution\n * across the character set recommended by RFC 7636.\n *\n * @returns {string} A 64-character URL-safe random string.\n */\nexport const generateCodeVerifier = (): string => {\n  const charset =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~\";\n  const n = charset.length;\n  const maxValidByte = 256 - (256 % n);\n  let result = \"\";\n  const requiredLength = 64;\n  const tempArray = new Uint8Array(1);\n\n  while (result.length < requiredLength) {\n    window.crypto.getRandomValues(tempArray);\n    const byte = tempArray[0];\n    if (byte < maxValidByte) {\n      result += charset.charAt(byte % n);\n    }\n  }\n  return result;\n};\n\n/**\n * Stores the PKCE code verifier in sessionStorage.\n * @param {string} verifier - The verifier to store.\n */\nexport const setStoredCodeVerifier = (verifier: string) => {\n  if (typeof window !== \"undefined\" && window.sessionStorage) {\n    window.sessionStorage.setItem(PKCE_STORAGE_KEY, verifier);\n  }\n};\n\n/**\n * Retrieves the PKCE code verifier from sessionStorage.\n * @returns {string | null}\n */\nexport const getStoredCodeVerifier = (): string | null => {\n  if (typeof window !== \"undefined\" && window.sessionStorage) {\n    return window.sessionStorage.getItem(PKCE_STORAGE_KEY);\n  }\n  return null;\n};\n\n/**\n * Removes the PKCE code verifier from sessionStorage.\n */\nexport const clearStoredCodeVerifier = () => {\n  if (typeof window !== \"undefined\" && window.sessionStorage) {\n    window.sessionStorage.removeItem(PKCE_STORAGE_KEY);\n  }\n};\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/SessionStorage.ts",
    "content": "/**\n * Options for SessionStorage\n *\n * @category SDK\n * @subcategory Internal\n * @property {string} keyName - The name of the sessionStorage session token entry set from the SDK.\n */\ninterface SessionStorageOptions {\n  keyName: string;\n}\n\n/**\n * A class to manage sessionStorage.\n *\n * @category SDK\n * @subcategory Internal\n * @param {SessionStorageOptions} options - The options that can be used.\n */\nexport class SessionStorage {\n  keyName: string;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(options: SessionStorageOptions) {\n    this.keyName = options.keyName;\n  }\n\n  /**\n   * Return the session token that was stored in the sessionStorage.\n   *\n   * @return {string}\n   */\n  getSessionToken(): string {\n    return sessionStorage.getItem(this.keyName);\n  }\n\n  /**\n   * Stores the session token in the sessionStorage.\n   *\n   * @param {string} token - The session token to be stored.\n   */\n  setSessionToken(token: string) {\n    sessionStorage.setItem(this.keyName, token);\n  }\n\n  /**\n   * Removes the session token used for authentication.\n   */\n  removeSessionToken() {\n    sessionStorage.removeItem(this.keyName);\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/Throttle.ts",
    "content": "/**\n * @interface\n * @category SDK\n * @subcategory Internal\n * @property {boolean=} leading - Whether to allow the function to be called on the leading edge of the wait timeout.\n * @property {boolean=} trailing - Whether to allow the function to be called on the trailing edge of the wait timeout.\n */\ninterface ThrottleOptions {\n  leading?: boolean;\n  trailing?: boolean;\n}\n\n// eslint-disable-next-line no-unused-vars\ntype ThrottledFunction<T extends (...args: any[]) => any> = (\n  // eslint-disable-next-line no-unused-vars\n  ...args: Parameters<T>\n) => void;\n\n/**\n * Provides throttle functionality.\n *\n * @hideconstructor\n * @category SDK\n * @subcategory Internal\n */\nexport class Throttle {\n  /**\n   * Throttles a function, ensuring that it can only be called once per `wait` milliseconds.\n   *\n   * @static\n   * @param {function} func - The function to throttle.\n   * @param {number} wait - The number of milliseconds to wait between function invocations.\n   * @param {ThrottleOptions} options - Optional configuration for the throttle.\n   * @returns {function} A throttled version of the original function.\n   */\n  // eslint-disable-next-line no-unused-vars,require-jsdoc\n  static throttle<T extends (...args: any[]) => any>(\n    func: T,\n    wait: number,\n    options: ThrottleOptions = {},\n  ): ThrottledFunction<T> {\n    const { leading = true, trailing = true } = options;\n    let context: any;\n    let args: any;\n    let timeoutID: number;\n    let previous = 0;\n\n    // This function is used to invoke the original function.\n    const executeThrottledFunction = () => {\n      // If 'leading' is false and this is not the first invocation of the throttled function, set 'previous' to 0 to\n      // ensure that the function is not called immediately.\n      previous = leading === false ? 0 : Date.now();\n      timeoutID = null;\n      // Invoke the original function.\n      func.apply(context, args);\n    };\n\n    // This is the throttled function that will be returned.\n    const throttled = function (...funcArgs: Parameters<T>) {\n      const now = Date.now();\n\n      // If this is the first time the throttled function is being called, and 'leading' is false,\n      // set 'previous' to the current time to ensure that the function is not called immediately.\n      if (!previous && leading === false) previous = now;\n\n      // The remaining wait time.\n      const remaining = wait - (now - previous);\n\n      // Save the context and arguments of the function call.\n      // eslint-disable-next-line no-invalid-this\n      context = this;\n      args = funcArgs;\n\n      // Check whether it's time to call the function immediately based on the leading and trailing options. If leading\n      // is enabled and there was no previous invocation, or if trailing is enabled and the wait time has already passed,\n      // the function will be invoked immediately.\n      if (remaining <= 0 || remaining > wait) {\n        // If there is a pending timeout, clear it.\n        if (timeoutID) {\n          window.clearTimeout(timeoutID);\n          timeoutID = null;\n        }\n\n        // Invoke the original function and update the previous timestamp.\n        previous = now;\n        func.apply(context, args);\n      } else if (!timeoutID && trailing !== false) {\n        // If there is no pending timeout and trailing is allowed, start a new timeout.\n        timeoutID = window.setTimeout(executeThrottledFunction, remaining);\n      }\n    };\n\n    return throttled;\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/WebauthnSupport.ts",
    "content": "/**\n * A class to check the browser's WebAuthn support.\n *\n * @hideconstructor\n * @category SDK\n * @subcategory Utilities\n */\nclass WebauthnSupport {\n  /**\n   * Does a simple check to test for the credential management API functions we need, and an indication of\n   * public key credential authentication support.\n   *\n   * @see https://developers.google.com/web/updates/2018/03/webauthn-credential-management\n   * @return boolean\n   */\n  static supported(): boolean {\n    return !!(\n      navigator.credentials &&\n      navigator.credentials.create &&\n      navigator.credentials.get &&\n      window.PublicKeyCredential\n    );\n  }\n\n  /**\n   * Checks whether a user-verifying platform authenticator is available.\n   *\n   * @return Promise<boolean>\n   */\n  static async isPlatformAuthenticatorAvailable(): Promise<boolean> {\n    if (\n      this.supported() &&\n      window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable\n    ) {\n      return window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();\n    }\n\n    return false;\n  }\n\n  /**\n   * Checks whether external CTAP2 security keys are supported.\n   *\n   * @return Promise<boolean>\n   */\n  static async isSecurityKeySupported(): Promise<boolean> {\n    if (\n      window.PublicKeyCredential !== undefined &&\n      // @ts-ignore\n      window.PublicKeyCredential.isExternalCTAP2SecurityKeySupported\n    ) {\n      // @ts-ignore\n      return window.PublicKeyCredential.isExternalCTAP2SecurityKeySupported();\n    }\n\n    return this.supported();\n  }\n\n  /**\n   * Checks whether autofill assisted requests are supported.\n   *\n   * @return Promise<boolean>\n   */\n  static async isConditionalMediationAvailable(): Promise<boolean> {\n    if (\n      // @ts-ignore\n      window.PublicKeyCredential &&\n      // @ts-ignore\n      window.PublicKeyCredential.isConditionalMediationAvailable\n    ) {\n      // @ts-ignore\n      return window.PublicKeyCredential.isConditionalMediationAvailable();\n    }\n\n    return false;\n  }\n}\n\nexport { WebauthnSupport };\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/client/Client.ts",
    "content": "import { HttpClient, HttpClientOptions } from \"./HttpClient\";\n\n/**\n * A class to be extended by the other client classes.\n *\n * @abstract\n * @category SDK\n * @subcategory Internal\n * @param {string} api - The URL of your Hanko API instance\n * @param {HttpClientOptions} options - The options that can be used\n */\nabstract class Client {\n  client: HttpClient;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(api: string, options: HttpClientOptions) {\n    /**\n     *  @public\n     *  @type {HttpClient}\n     */\n    this.client = new HttpClient(api, options);\n  }\n}\n\nexport { Client };\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/client/HttpClient.ts",
    "content": "import { RequestTimeoutError, TechnicalError } from \"../Errors\";\nimport { Dispatcher } from \"../events/Dispatcher\";\nimport { Cookie } from \"../Cookie\";\nimport { SessionStorage } from \"../SessionStorage\";\nimport { CookieAttributes } from \"js-cookie\";\nimport { HankoOptions } from \"../../Hanko\";\n\nexport type SessionTokenLocation = \"cookie\" | \"sessionStorage\";\n\n/**\n * This class wraps an XMLHttpRequest to maintain compatibility with the fetch API.\n *\n * @category SDK\n * @subcategory Internal\n * @param {XMLHttpRequest} xhr - The request to be wrapped.\n * @see HttpClient\n */\nclass Headers {\n  _xhr: XMLHttpRequest;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(xhr: XMLHttpRequest) {\n    this._xhr = xhr;\n  }\n\n  /**\n   * Returns the response header with the given name.\n   *\n   * @param {string} name\n   * @return {string}\n   */\n  getResponseHeader(name: string) {\n    return this._xhr.getResponseHeader(name);\n  }\n}\n\n/**\n * This class wraps an XMLHttpRequest to maintain compatibility with the fetch API.\n *\n * @category SDK\n * @subcategory Internal\n * @param {XMLHttpRequest} xhr - The request to be wrapped.\n * @see HttpClient\n */\nclass Response {\n  headers: Headers;\n  ok: boolean;\n  status: number;\n  statusText: string;\n  url: string;\n  _decodedJSON: any;\n  xhr: XMLHttpRequest;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(xhr: XMLHttpRequest) {\n    /**\n     *  @public\n     *  @type {Headers}\n     */\n    this.headers = new Headers(xhr);\n    /**\n     *  @public\n     *  @type {boolean}\n     */\n    this.ok = xhr.status >= 200 && xhr.status <= 299;\n    /**\n     *  @public\n     *  @type {number}\n     */\n    this.status = xhr.status;\n    /**\n     *  @public\n     *  @type {string}\n     */\n    this.statusText = xhr.statusText;\n    /**\n     *  @public\n     *  @type {string}\n     */\n    this.url = xhr.responseURL;\n    /**\n     *  @private\n     *  @type {XMLHttpRequest}\n     */\n    this.xhr = xhr;\n  }\n\n  /**\n   * Returns the JSON decoded response.\n   *\n   * @return {any}\n   */\n  json() {\n    if (!this._decodedJSON) {\n      this._decodedJSON = JSON.parse(this.xhr.response);\n    }\n    return this._decodedJSON;\n  }\n\n  /**\n   * Returns the response header value with the given `name` as a number. When the value is not a number the return\n   * value will be 0.\n   *\n   * @param {string} name - The name of the header field\n   * @return {number}\n   */\n  parseNumericHeader(name: string): number {\n    const result = parseInt(this.headers.getResponseHeader(name), 10);\n    return isNaN(result) ? 0 : result;\n  }\n}\n\n/**\n * Options for the HttpClient\n *\n * @category SDK\n * @subcategory Internal\n * @property {number=} timeout - The http request timeout in milliseconds.\n * @property {string} cookieName - The name of the session cookie set from the SDK.\n * @property {string=} cookieDomain - The domain where cookie set from the SDK is available. Defaults to the domain of the page where the cookie was created.\n * @property {string?} lang - The language used by the client(s) to convey to the Hanko API the language to use -\n *                           e.g. for translating outgoing emails - in a custom header (X-Language).\n */\nexport interface HttpClientOptions {\n  timeout?: number;\n  cookieName?: string;\n  cookieDomain?: string;\n  lang?: string;\n  sessionTokenLocation?: SessionTokenLocation;\n}\n\n/**\n * Internally used for communication with the Hanko API. It also handles authorization tokens to enable authorized\n * requests.\n *\n * Currently, there is an issue with Safari and on iOS 15 devices where decoding a JSON response via the fetch API\n * breaks the user gesture and the user is not able to use the authenticator. Therefore, this class uses XMLHttpRequests\n * instead of the fetch API, but maintains compatibility by wrapping the XMLHttpRequests. So, if the issues are fixed,\n * we can easily return to the fetch API.\n *\n * @category SDK\n * @subcategory Internal\n * @param {string} api - The URL of your Hanko API instance\n * @param {HttpClientOptions} options - The options the HttpClient must be provided\n */\nclass HttpClient {\n  timeout: number;\n  api: string;\n  dispatcher: Dispatcher;\n  cookie: Cookie;\n  sessionTokenStorage: SessionStorage;\n  lang: string;\n  sessionTokenLocation: SessionTokenLocation;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(api: string, options: HankoOptions) {\n    this.api = api;\n    this.timeout = options.timeout ?? 13000;\n    this.dispatcher = new Dispatcher();\n    this.cookie = new Cookie({ ...options });\n    this.sessionTokenStorage = new SessionStorage({\n      keyName: options.cookieName,\n    });\n    this.lang = options.lang;\n    this.sessionTokenLocation = options.sessionTokenLocation;\n  }\n\n  // eslint-disable-next-line require-jsdoc\n  _fetch(path: string, options: RequestInit, xhr = new XMLHttpRequest()) {\n    const self = this;\n    const url = this.api + path;\n    const timeout = this.timeout;\n    const bearerToken = this.getAuthToken();\n    const lang = this.lang;\n\n    return new Promise<Response>(function (resolve, reject) {\n      xhr.open(options.method, url, true);\n      xhr.setRequestHeader(\"Accept\", \"application/json\");\n      xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n      xhr.setRequestHeader(\"X-Language\", lang);\n\n      if (bearerToken) {\n        xhr.setRequestHeader(\"Authorization\", `Bearer ${bearerToken}`);\n      }\n\n      xhr.timeout = timeout;\n      xhr.withCredentials = true;\n      xhr.onload = () => {\n        self.processHeaders(xhr);\n        resolve(new Response(xhr));\n      };\n\n      xhr.onerror = () => {\n        reject(new TechnicalError());\n      };\n\n      xhr.ontimeout = () => {\n        reject(new RequestTimeoutError());\n      };\n\n      xhr.send(options.body ? options.body.toString() : null);\n    });\n  }\n\n  /**\n   * Processes the response headers on login and extracts the JWT and expiration time.\n   *\n   * @param {XMLHttpRequest} xhr - The xhr object.\n   */\n  processHeaders(xhr: XMLHttpRequest) {\n    let jwt = \"\";\n    let expirationSeconds = 0;\n    let retention = \"\";\n\n    xhr\n      .getAllResponseHeaders()\n      .split(\"\\r\\n\")\n      .forEach((h) => {\n        const header = h.toLowerCase();\n        if (header.startsWith(\"x-auth-token\")) {\n          jwt = xhr.getResponseHeader(\"X-Auth-Token\");\n        } else if (header.startsWith(\"x-session-lifetime\")) {\n          expirationSeconds = parseInt(\n            xhr.getResponseHeader(\"X-Session-Lifetime\"),\n            10,\n          );\n        } else if (header.startsWith(\"x-session-retention\")) {\n          retention = xhr.getResponseHeader(\"X-Session-Retention\");\n        }\n      });\n\n    if (jwt) {\n      const https = new RegExp(\"^https://\");\n      const secure =\n        !!this.api.match(https) && !!window.location.href.match(https);\n\n      const expires =\n        retention === \"session\"\n          ? undefined\n          : new Date(new Date().getTime() + expirationSeconds * 1000);\n\n      this.setAuthToken(jwt, { secure, expires });\n    }\n  }\n\n  /**\n   * Performs a GET request.\n   *\n   * @param {string} path - The path to the requested resource.\n   * @return {Promise<Response>}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   */\n  get(path: string) {\n    return this._fetch(path, { method: \"GET\" });\n  }\n\n  /**\n   * Performs a POST request.\n   *\n   * @param {string} path - The path to the requested resource.\n   * @param {any=} body - The request body.\n   * @return {Promise<Response>}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   */\n  post(path: string, body?: any) {\n    return this._fetch(path, {\n      method: \"POST\",\n      body: JSON.stringify(body),\n    });\n  }\n\n  /**\n   * Performs a PUT request.\n   *\n   * @param {string} path - The path to the requested resource.\n   * @param {any=} body - The request body.\n   * @return {Promise<Response>}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   */\n  put(path: string, body?: any) {\n    return this._fetch(path, {\n      method: \"PUT\",\n      body: JSON.stringify(body),\n    });\n  }\n\n  /**\n   * Performs a PATCH request.\n   *\n   * @param {string} path - The path to the requested resource.\n   * @param {any=} body - The request body.\n   * @return {Promise<Response>}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   */\n  patch(path: string, body?: any) {\n    return this._fetch(path, {\n      method: \"PATCH\",\n      body: JSON.stringify(body),\n    });\n  }\n\n  /**\n   * Performs a DELETE request.\n   *\n   * @param {string} path - The path to the requested resource.\n   * @return {Promise<Response>}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   */\n  delete(path: string) {\n    return this._fetch(path, {\n      method: \"DELETE\",\n    });\n  }\n\n  /**\n   * Returns the session token either from the cookie or the sessionStorage.\n   * @private\n   * @return {string}\n   */\n  private getAuthToken(): string {\n    let token = \"\";\n    switch (this.sessionTokenLocation) {\n      case \"cookie\":\n        token = this.cookie.getAuthCookie();\n        break;\n      case \"sessionStorage\":\n        token = this.sessionTokenStorage.getSessionToken();\n        break;\n      default:\n        token = this.cookie.getAuthCookie();\n        break;\n    }\n    return token;\n  }\n\n  /**\n   * Stores the session token either in a cookie or in the sessionStorage depending on the configuration.\n   * @param {string} token - The session token to be stored.\n   * @param {CookieAttributes} options - Options for setting the auth cookie.\n   * @private\n   */\n  private setAuthToken(token: string, options: CookieAttributes) {\n    switch (this.sessionTokenLocation) {\n      case \"cookie\":\n        return this.cookie.setAuthCookie(token, options);\n      case \"sessionStorage\":\n        return this.sessionTokenStorage.setSessionToken(token);\n      default:\n        return this.cookie.setAuthCookie(token, options);\n    }\n  }\n}\n\nexport { Headers, Response, HttpClient };\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/client/SessionClient.ts",
    "content": "import { Client } from \"./Client\";\nimport { SessionCheckResponse } from \"../Dto\";\nimport { TechnicalError } from \"../Errors\";\n\n/**\n * A class that handles communication with the Hanko API for the purposes\n * of sessions.\n *\n * @constructor\n * @category SDK\n * @subcategory Clients\n * @extends {Client}\n */\nexport class SessionClient extends Client {\n  /**\n   * Checks if the current session is still valid.\n   *\n   * @return {Promise<SessionCheckResponse>}\n   * @throws {TechnicalError}\n   */\n  async validate(): Promise<SessionCheckResponse> {\n    const response = await this.client.get(\"/sessions/validate\");\n\n    if (!response.ok) {\n      throw new TechnicalError();\n    }\n\n    return await response.json();\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/client/UserClient.ts",
    "content": "import { TechnicalError, UnauthorizedError } from \"../Errors\";\nimport { Client } from \"./Client\";\nimport { User } from \"../flow-api/types/payload\";\nimport { Me } from \"../Dto\";\n\n/**\n * A class to manage user information.\n *\n * @category SDK\n * @subcategory Clients\n * @extends {Client}\n */\nclass UserClient extends Client {\n  /**\n   * Fetches the current user.\n   *\n   * @return {Promise<User>}\n   * @throws {UnauthorizedError}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   * @deprecated\n   * @see https://docs.hanko.io/api/public#tag/User-Management/operation/IsUserAuthorized\n   * @see https://docs.hanko.io/api/public#tag/User-Management/operation/listUser\n   */\n  async getCurrent(): Promise<User> {\n    const meResponse = await this.client.get(\"/me\");\n\n    if (meResponse.status === 401) {\n      this.client.dispatcher.dispatchSessionExpiredEvent();\n      throw new UnauthorizedError();\n    } else if (!meResponse.ok) {\n      throw new TechnicalError();\n    }\n\n    const me: Me = meResponse.json();\n    const userResponse = await this.client.get(`/users/${me.id}`);\n\n    if (userResponse.status === 401) {\n      this.client.dispatcher.dispatchSessionExpiredEvent();\n      throw new UnauthorizedError();\n    } else if (!userResponse.ok) {\n      throw new TechnicalError();\n    }\n\n    return userResponse.json();\n  }\n\n  /**\n   * Fetches the current user.\n   *\n   * @return {Promise<User>}\n   * @throws {UnauthorizedError}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   */\n  async getCurrentUser(): Promise<User> {\n    const meResponse = await this.client.get(\"/me\");\n\n    if (meResponse.status === 401) {\n      this.client.dispatcher.dispatchSessionExpiredEvent();\n      throw new UnauthorizedError();\n    } else if (!meResponse.ok) {\n      throw new TechnicalError();\n    }\n\n    return meResponse.json();\n  }\n\n  /**\n   * Logs out the current user and expires the existing session cookie. A valid session cookie is required to call the logout endpoint.\n   *\n   * @return {Promise<void>}\n   * @throws {RequestTimeoutError}\n   * @throws {TechnicalError}\n   */\n  async logout(): Promise<void> {\n    const logoutResponse = await this.client.post(\"/logout\");\n\n    // For cross-domain operations, the frontend SDK creates the cookie by reading the \"X-Auth-Token\" header, and\n    // \"Set-Cookie\" headers sent by the backend have no effect due to the browser's security policy, which means that\n    // the cookie must also be removed client-side in that case.\n    this.client.sessionTokenStorage.removeSessionToken();\n    this.client.cookie.removeAuthCookie();\n    this.client.dispatcher.dispatchUserLoggedOutEvent();\n\n    if (logoutResponse.status === 401) {\n      // The user is logged out already\n      return;\n    } else if (!logoutResponse.ok) {\n      throw new TechnicalError();\n    }\n  }\n}\n\nexport { UserClient };\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/CustomEvents.ts",
    "content": "import { Claims } from \"../Dto\";\nimport { AnyState } from \"../flow-api/types/flow\";\n\n/**\n * The type of the `hanko-session-created` event.\n * @typedef {string} sessionCreatedType\n * @memberOf Listener\n */\nexport const sessionCreatedType: \"hanko-session-created\" =\n  \"hanko-session-created\";\n\n/**\n * The type of the `hanko-session-expired` event.\n * @typedef {string} sessionExpiredType\n * @memberOf Listener\n */\nexport const sessionExpiredType: \"hanko-session-expired\" =\n  \"hanko-session-expired\";\n\n/**\n * The type of the `hanko-user-logged-out` event.\n * @typedef {string} userLoggedOutType\n * @memberOf Listener\n */\nexport const userLoggedOutType: \"hanko-user-logged-out\" =\n  \"hanko-user-logged-out\";\n\n/**\n * The type of the `hanko-user-deleted` event.\n * @typedef {string} userDeletedType\n * @memberOf Listener\n */\nexport const userDeletedType: \"hanko-user-deleted\" = \"hanko-user-deleted\";\n\n/**\n * The type of the `hanko-user-logged-in` event.\n * @typedef {string} userLoggedInType\n * @memberOf Listener\n */\nexport const userLoggedInType: \"hanko-user-logged-in\" = \"hanko-user-logged-in\";\n\n/**\n * The type of the `hanko-user-created` event.\n * @typedef {string} userCreatedType\n * @memberOf Listener\n */\nexport const userCreatedType: \"hanko-user-created\" = \"hanko-user-created\";\n\n/**\n * The type of the `hanko-after-state-change` event.\n * @typedef {string} flowAfterStateChangeType\n * @memberOf Listener\n */\nexport const flowAfterStateChangeType: \"hanko-after-state-change\" =\n  \"hanko-after-state-change\";\n\n/**\n * The type of the `hanko-before-state-change` event.\n * @typedef {string} flowBeforeStateChangeType\n * @memberOf Listener\n */\nexport const flowBeforeStateChangeType: \"hanko-before-state-change\" =\n  \"hanko-before-state-change\";\n\n/**\n * The type of the `hanko-flow-error` event.\n * @typedef {string} flowErrorType\n * @memberOf Listener\n */\nexport const flowErrorType: \"hanko-flow-error\" = \"hanko-flow-error\";\n\n/**\n * The data passed in the `hanko-session-created` or `hanko-session-resumed` event.\n *\n * @interface\n * @category SDK\n * @subcategory Events\n * @property {number} expirationSeconds - This property is deprecated. The number of seconds until the JWT expires.\n * @property {Claims} claims - The JSON web token associated with the session. Only present when the Hanko-API allows the JWT to be accessible client-side.\n */\nexport interface SessionDetail {\n  claims: Claims;\n  expirationSeconds: number; // deprecated\n}\n\nexport interface FlowErrorDetail {\n  error: Error;\n}\n\nexport interface FlowDetail {\n  state: AnyState;\n}\n\n/**\n * A custom event that includes a detail object.\n *\n * @category SDK\n * @subcategory Events\n * @extends CustomEvent\n * @ignore\n * @param {string} type - The type of the event.\n * @param {T} detail - The detail object to include in the event.\n */\nexport class CustomEventWithDetail<T> extends CustomEvent<T> {\n  // eslint-disable-next-line require-jsdoc\n  constructor(type: string, detail: T) {\n    super(type, { detail });\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/Dispatcher.ts",
    "content": "import {\n  SessionDetail,\n  CustomEventWithDetail,\n  sessionCreatedType,\n  sessionExpiredType,\n  userDeletedType,\n  userLoggedOutType,\n  flowAfterStateChangeType,\n  FlowDetail,\n  flowBeforeStateChangeType,\n} from \"./CustomEvents\";\n\n/**\n * A class that dispatches custom events.\n *\n * @category SDK\n * @subcategory Internal\n */\nexport class Dispatcher {\n  _dispatchEvent = document.dispatchEvent.bind(document);\n\n  /**\n   * Dispatches a custom event.\n   *\n   * @param {string} type\n   * @param {T} detail\n   * @private\n   */\n  private dispatch<T>(type: string, detail: T) {\n    this._dispatchEvent(new CustomEventWithDetail(type, detail));\n  }\n\n  /**\n   * Dispatches a \"hanko-session-created\" event to the document with the specified detail.\n   *\n   * @param {SessionDetail} detail - The event detail.\n   */\n  public dispatchSessionCreatedEvent(detail: SessionDetail) {\n    this.dispatch(sessionCreatedType, detail);\n  }\n\n  /**\n   * Dispatches a \"hanko-session-expired\" event to the document.\n   */\n  public dispatchSessionExpiredEvent() {\n    this.dispatch(sessionExpiredType, null);\n  }\n\n  /**\n   * Dispatches a \"hanko-user-logged-out\" event to the document.\n   */\n  public dispatchUserLoggedOutEvent() {\n    this.dispatch(userLoggedOutType, null);\n  }\n\n  /**\n   * Dispatches a \"hanko-user-deleted\" event to the document.\n   */\n  public dispatchUserDeletedEvent() {\n    this.dispatch(userDeletedType, null);\n  }\n\n  /**\n   * Dispatches a \"hanko-after-state-change\" event to the document.\n   */\n  public dispatchAfterStateChangeEvent(detail: FlowDetail) {\n    this.dispatch(flowAfterStateChangeType, detail);\n  }\n\n  /**\n   * Dispatches a \"hanko-before-state-change\" event to the document.\n   */\n  public dispatchBeforeStateChangeEvent(detail: FlowDetail) {\n    this.dispatch(flowBeforeStateChangeType, detail);\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/Listener.ts",
    "content": "import { Throttle } from \"../Throttle\";\nimport {\n  CustomEventWithDetail,\n  SessionDetail,\n  FlowDetail,\n  sessionCreatedType,\n  sessionExpiredType,\n  userDeletedType,\n  userLoggedOutType,\n  flowAfterStateChangeType,\n  flowBeforeStateChangeType,\n  flowErrorType,\n  FlowErrorDetail,\n} from \"./CustomEvents\";\n\n/**\n * A callback function to be executed when an event is triggered.\n *\n * @alias CallbackFunc\n * @typedef {function} CallbackFunc\n * @memberOf Listener\n */\n// eslint-disable-next-line no-unused-vars\ntype CallbackFunc<T> = (detail: T) => any;\n\n/**\n * A wrapped callback function that will execute the original callback.\n *\n * @ignore\n * @param {T} event - The event object passed in the event.\n */\n// eslint-disable-next-line no-unused-vars\ntype WrappedCallback<T> = (event: CustomEventWithDetail<T>) => void;\n\n/**\n * A function returned when adding an event listener. The function can be called to remove the corresponding event\n * listener.\n *\n * @alias CleanupFunc\n * @typedef {function} CleanupFunc\n * @memberOf Listener\n */\ntype CleanupFunc = () => void;\n\n/**\n * @interface\n * @ignore\n * @property {Function} callback - The function to be executed.\n * @property {boolean=} once - Whether the event listener should be removed after being called once.\n */\ninterface EventListenerParams<T> {\n  callback: CallbackFunc<T>;\n  once?: boolean;\n}\n\n/**\n * @interface\n * @ignore\n * @extends {EventListenerParams<T>}\n * @property {string} type - The type of the event.\n * @property {boolean=} throttle - Whether the event listener should be throttled.\n */\ninterface EventListenerWithTypeParams<T> extends EventListenerParams<T> {\n  type: string;\n  throttle?: boolean;\n}\n\n/**\n * A class to bind event listener for custom events.\n *\n * @category SDK\n * @subcategory Events\n */\nexport class Listener {\n  public throttleLimit = 1000;\n  _addEventListener = document.addEventListener.bind(document);\n  _removeEventListener = document.removeEventListener.bind(document);\n  _throttle = Throttle.throttle;\n\n  /**\n   * Wraps the given callback.\n   *\n   * @param callback\n   * @param throttle\n   * @private\n   * @return {WrappedCallback}\n   */\n  private wrapCallback<T>(\n    callback: CallbackFunc<T>,\n    throttle: boolean,\n  ): WrappedCallback<T> {\n    // The function that will be called when the event is triggered.\n    const wrappedCallback = (event: CustomEventWithDetail<T>) => {\n      callback(event.detail);\n    };\n\n    // Throttle the listener if multiple SDK instances could trigger the same event at the same time,\n    // but the callback function should only be executed once.\n    if (throttle) {\n      return this._throttle(wrappedCallback, this.throttleLimit, {\n        leading: true,\n        trailing: false,\n      });\n    }\n\n    return wrappedCallback;\n  }\n\n  /**\n   * Adds an event listener with the specified type, callback function, and options.\n   *\n   * @private\n   * @param {EventListenerWithTypeParams<T>} params - The parameters for the event listener.\n   * @returns {CleanupFunc} This function can be called to remove the event listener.\n   */\n  private addEventListenerWithType<T>({\n    type,\n    callback,\n    once = false,\n    throttle = false,\n  }: EventListenerWithTypeParams<T>): CleanupFunc {\n    const wrappedCallback = this.wrapCallback(callback, throttle);\n    this._addEventListener(type, wrappedCallback, { once });\n    return () => this._removeEventListener(type, wrappedCallback);\n  }\n\n  /**\n   * Maps the parameters for an event listener to the `EventListenerWithTypeParams` interface.\n   *\n   * @static\n   * @private\n   * @param {string} type - The type of the event.\n   * @param {EventListenerParams<T>} params - The parameters for the event listener.\n   * @param {boolean} [throttle=false] - Whether the event listener should be throttled.\n   * @returns {EventListenerWithTypeParams<T>}\n   **/\n  private static mapAddEventListenerParams<T>(\n    type: string,\n    { once, callback }: EventListenerParams<T>,\n    throttle?: boolean,\n  ): EventListenerWithTypeParams<T> {\n    return {\n      type,\n      callback,\n      once,\n      throttle,\n    };\n  }\n\n  /**\n   * Adds an event listener with the specified type, callback function, and options.\n   *\n   * @private\n   * @param {string} type - The type of the event.\n   * @param {EventListenerParams<T>} params - The parameters for the event listener.\n   * @param {boolean=} throttle - Whether the event listener should be throttled.\n   * @returns {CleanupFunc} This function can be called to remove the event listener.\n   */\n  private addEventListener<T>(\n    type: string,\n    params: EventListenerParams<T>,\n    throttle?: boolean,\n  ) {\n    return this.addEventListenerWithType(\n      Listener.mapAddEventListenerParams(type, params, throttle),\n    );\n  }\n\n  /**\n   * Adds an event listener for \"hanko-session-created\" events. Will be triggered across all browser windows, when the user\n   * logs in, or when the page has been loaded or refreshed and there is a valid session.\n   *\n   * @param {CallbackFunc<SessionDetail>} callback - The function to be called when the event is triggered.\n   * @param {boolean=} once - Whether the event listener should be removed after being called once.\n   * @returns {CleanupFunc} This function can be called to remove the event listener.\n   */\n  public onSessionCreated(\n    callback: CallbackFunc<SessionDetail>,\n    once?: boolean,\n  ): CleanupFunc {\n    return this.addEventListener(sessionCreatedType, { callback, once }, true);\n  }\n\n  /**\n   * Adds an event listener for \"hanko-session-expired\" events. The event will be triggered across all browser windows\n   * as soon as the current JWT expires or the user logs out. It also triggers, when the user deletes the account in\n   * another window.\n   *\n   * @param {CallbackFunc<null>} callback - The function to be called when the event is triggered.\n   * @param {boolean=} once - Whether the event listener should be removed after being called once.\n   * @returns {CleanupFunc} This function can be called to remove the event listener.\n   */\n  public onSessionExpired(\n    callback: CallbackFunc<null>,\n    once?: boolean,\n  ): CleanupFunc {\n    return this.addEventListener(sessionExpiredType, { callback, once }, true);\n  }\n\n  /**\n   * Adds an event listener for hanko-user-deleted events. The event triggers, when the user has deleted the account in\n   * the browser window where the deletion happened.\n   *\n   * @param {CallbackFunc<null>} callback - The function to be called when the event is triggered.\n   * @param {boolean=} once - Whether the event listener should be removed after being called once.\n   * @returns {CleanupFunc} This function can be called to remove the event listener.\n   */\n  public onUserLoggedOut(\n    callback: CallbackFunc<null>,\n    once?: boolean,\n  ): CleanupFunc {\n    return this.addEventListener(userLoggedOutType, { callback, once });\n  }\n\n  /**\n   * Adds an event listener for hanko-user-deleted events. The event triggers, when the user has deleted the account.\n   *\n   * @param {CallbackFunc<null>} callback - The function to be called when the event is triggered.\n   * @param {boolean=} once - Whether the event listener should be removed after being called once.\n   * @returns {CleanupFunc} This function can be called to remove the event listener.\n   */\n  public onUserDeleted(\n    callback: CallbackFunc<null>,\n    once?: boolean,\n  ): CleanupFunc {\n    return this.addEventListener(userDeletedType, { callback, once });\n  }\n\n  public onAfterStateChange(\n    callback: CallbackFunc<FlowDetail>,\n    once?: boolean,\n  ): CleanupFunc {\n    return this.addEventListener(\n      flowAfterStateChangeType,\n      { callback, once },\n      false,\n    );\n  }\n\n  public onBeforeStateChange(\n    callback: CallbackFunc<FlowDetail>,\n    once?: boolean,\n  ): CleanupFunc {\n    return this.addEventListener(\n      flowBeforeStateChangeType,\n      { callback, once },\n      false,\n    );\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/Relay.ts",
    "content": "import { Listener } from \"./Listener\";\nimport { Dispatcher } from \"./Dispatcher\";\nimport { SessionClient } from \"../client/SessionClient\";\nimport { SessionState } from \"./SessionState\";\nimport { WindowActivityManager } from \"./WindowActivityManager\";\nimport { Scheduler, SessionCheckResult } from \"./Scheduler\";\nimport { SessionTokenLocation } from \"../client/HttpClient\";\nimport { SessionChannel, BroadcastMessage } from \"./SessionChannel\";\nimport { HankoOptions } from \"../../Hanko\";\n\n/**\n * A class that manages session checks, dispatches events based on session status,\n * and uses broadcast channels for inter-tab communication.\n *\n * @category SDK\n * @subcategory Internal\n * @extends Dispatcher\n * @param {string} api - The API endpoint URL.\n * @param {HankoOptions} options - The internal configuration options of the SDK.\n */\nexport class Relay extends Dispatcher {\n  listener = new Listener(); // Listener for session-related events.\n  private readonly checkInterval: number = 30000; // Interval for session validity checks in milliseconds.\n  private readonly client: SessionClient; // Client for session validation.\n  private readonly sessionState: SessionState; // Manages session-related states.\n  private readonly windowActivityManager: WindowActivityManager; // Manages window activity states.\n  private readonly scheduler: Scheduler; //  Schedules session validity checks.\n  private readonly sessionChannel: SessionChannel; // Handles inter-tab communication via broadcast channels.\n  private isLoggedIn: boolean;\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(api: string, options: HankoOptions) {\n    super();\n    this.client = new SessionClient(api, options);\n\n    if (options.sessionCheckInterval) {\n      this.checkInterval =\n        options.sessionCheckInterval < 3000\n          ? 3000\n          : options.sessionCheckInterval;\n    }\n\n    this.sessionState = new SessionState(`${options.cookieName}_session_state`);\n    this.sessionChannel = new SessionChannel(\n      this.getSessionCheckChannelName(\n        options.sessionTokenLocation,\n        options.sessionCheckChannelName,\n      ),\n      () => this.onChannelSessionExpired(),\n      (msg) => this.onChannelSessionCreated(msg),\n      () => this.onChannelLeadershipRequested(),\n    );\n    this.scheduler = new Scheduler(\n      this.checkInterval,\n      () => this.checkSession(),\n      () => this.onSessionExpired(),\n    );\n    this.windowActivityManager = new WindowActivityManager(\n      () => this.startSessionCheck(),\n      () => this.scheduler.stop(),\n    );\n\n    const now = Date.now();\n    const { expiration } = this.sessionState.load();\n\n    this.isLoggedIn = now < expiration;\n    this.initializeEventListeners();\n    this.startSessionCheck();\n  }\n\n  /**\n   * Sets up all event listeners and initializes session management.\n   * This method is crucial for ensuring the session is monitored across all tabs.\n   * @private\n   */\n  private initializeEventListeners(): void {\n    // Listen for session creation events\n    this.listener.onSessionCreated((detail) => {\n      const { claims } = detail;\n      const expiration = Date.parse(claims.expiration);\n      const lastCheck = Date.now();\n\n      this.isLoggedIn = true;\n      this.sessionState.save({ expiration, lastCheck }); // Save initial session state\n      this.sessionChannel.post({ action: \"sessionCreated\", claims }); // Inform other tabs\n      this.startSessionCheck(); // Begin session checks now that a user is logged in\n    });\n\n    // Listen for user logout events\n    this.listener.onUserLoggedOut(() => {\n      this.isLoggedIn = false;\n      this.sessionChannel.post({ action: \"sessionExpired\" }); // Inform other tabs session ended\n      this.sessionState.save(null);\n      this.scheduler.stop();\n    });\n\n    window.addEventListener(\"beforeunload\", () => this.scheduler.stop());\n  }\n\n  /**\n   * Initiates session checking based on the last check time.\n   * This method decides when the next check should occur to balance between performance and freshness.\n   * @private\n   */\n  private startSessionCheck(): void {\n    if (this.windowActivityManager.hasFocus()) {\n      this.sessionChannel.post({ action: \"requestLeadership\" }); // Inform other tabs this tab is now checking\n    } else {\n      return;\n    }\n\n    if (this.scheduler.isRunning()) {\n      return;\n    }\n\n    const { lastCheck, expiration } = this.sessionState.load();\n\n    if (this.isLoggedIn) {\n      this.scheduler.start(lastCheck, expiration);\n    }\n  }\n\n  /**\n   * Validates the current session and updates session information.\n   * This method checks if the session is still valid and updates local data accordingly.\n   * @returns {Promise<SessionCheckResult>} - A promise that resolves with the session check result.\n   * @private\n   */\n  private async checkSession(): Promise<SessionCheckResult> {\n    const lastCheck = Date.now();\n    // eslint-disable-next-line camelcase\n    const { is_valid, claims, expiration_time } = await this.client.validate();\n\n    // eslint-disable-next-line camelcase\n    const expiration = expiration_time ? Date.parse(expiration_time) : 0;\n\n    // eslint-disable-next-line camelcase\n    if (!is_valid && this.isLoggedIn) {\n      this.dispatchSessionExpiredEvent();\n    }\n\n    // eslint-disable-next-line camelcase\n    if (is_valid) {\n      this.isLoggedIn = true;\n      this.sessionState.save({ lastCheck, expiration });\n    } else {\n      this.isLoggedIn = false;\n      this.sessionState.save(null);\n      this.sessionChannel.post({ action: \"sessionExpired\" }); // Inform other tabs\n    }\n\n    return {\n      // eslint-disable-next-line camelcase\n      is_valid,\n      claims,\n      expiration,\n    };\n  }\n\n  /**\n   * Resets session-related states when a session expires.\n   * Ensures that authentication state is cleared and an expiration event is dispatched.\n   * Assumes the user is logged out by default if the session state is unknown.\n   * @private\n   */\n  private onSessionExpired() {\n    if (this.isLoggedIn) {\n      this.isLoggedIn = false;\n      this.sessionState.save(null);\n      this.sessionChannel.post({ action: \"sessionExpired\" }); // Inform other tabs\n      this.dispatchSessionExpiredEvent();\n    }\n  }\n\n  /**\n   * Handles session expired events from broadcast messages.\n   * @private\n   */\n  private onChannelSessionExpired() {\n    if (this.isLoggedIn) {\n      this.isLoggedIn = false;\n      this.dispatchSessionExpiredEvent();\n    }\n  }\n\n  /**\n   * Handles session creation events from broadcast messages.\n   * @param {BroadcastMessage} msg - The broadcast message containing session details.\n   * @private\n   */\n  private onChannelSessionCreated(msg: BroadcastMessage) {\n    const { claims } = msg;\n    const now = Date.now();\n    const expiration = Date.parse(claims.expiration);\n    const expirationSeconds = expiration - now;\n\n    this.isLoggedIn = true;\n    this.dispatchSessionCreatedEvent({\n      claims,\n      expirationSeconds, // deprecated\n    });\n  }\n\n  /**\n   * Handles leadership requests from other tabs.\n   * @private\n   */\n  private onChannelLeadershipRequested() {\n    if (!this.windowActivityManager.hasFocus()) {\n      this.scheduler.stop();\n    }\n  }\n\n  /**\n   * Retrieves or generates the session check channel name based on the session token storage location.\n   *\n   * - If the `sessionTokenLocation` is `\"cookie\"`, the provided `sessionCheckChannelName` is returned as-is.\n   * - If the `sessionTokenLocation` is `\"sessionStorage\"`, the function attempts to retrieve the channel name from\n   *   `sessionStorage`. If none is found, a new name is generated with the value of `sessionCheckChannelName` as a prefix and a random number,\n   *   then stored in `sessionStorage` for future use.\n   *\n   * @param sessionTokenLocation - Indicates where the session token is stored, either `\"cookie\"` or `\"sessionStorage\"`.\n   * @param sessionCheckChannelName - The name or prefix used for the session check channel.\n   * @returns The resolved session check channel name, or `undefined` if not applicable.\n   * @private\n   */\n  private getSessionCheckChannelName(\n    sessionTokenLocation: SessionTokenLocation,\n    sessionCheckChannelName?: string,\n  ): string | undefined {\n    if (sessionTokenLocation !== \"sessionStorage\") {\n      return sessionCheckChannelName;\n    }\n    let channelName = sessionStorage.getItem(\"sessionCheckChannelName\");\n    if (\n      channelName === null ||\n      channelName === undefined ||\n      channelName === \"\"\n    ) {\n      channelName = `${sessionCheckChannelName}-${\n        Math.floor(Math.random() * 100) + 1\n      }`;\n      sessionStorage.setItem(\"sessionCheckChannelName\", channelName);\n    }\n    return channelName;\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/Scheduler.ts",
    "content": "import { SessionCheckResponse } from \"../Dto\";\n\n// Type representing data returned by the session check callback.\nexport type SessionCheckResult =\n  | (Omit<SessionCheckResponse, \"expiration_time\"> & {\n      expiration: number;\n    })\n  | null;\n\n/**\n * Callback type for performing a session check.\n * @ignore\n */\ntype SessionCheckCallback = () => Promise<SessionCheckResult>;\n\n/**\n * Callback type for handling session timeout events.\n * @ignore\n */\ntype SessionExpiredCallback = () => void;\n\n/**\n * Manages scheduling for periodic and timeout-based session checks.\n *\n * @category SDK\n * @subcategory Internal\n * @param {number} checkInterval - The interval in milliseconds between periodic session checks.\n * @param {SessionCheckCallback} checkSession - The callback function to perform a session check.\n * @param {SessionExpiredCallback} onSessionExpired - The callback function to handle session timeout events.\n */\nexport class Scheduler {\n  private intervalID: ReturnType<typeof setInterval> | null = null; // Identifier for the periodic check interval.\n  private timeoutID: ReturnType<typeof setTimeout> | null = null; // Identifier for the session expiration timeout.\n  private readonly checkInterval: number; // The interval between periodic session checks.\n  private readonly checkSession: SessionCheckCallback; // The callback function to perform a session check.\n  private readonly onSessionExpired: SessionExpiredCallback; // The callback function to handle session expired events.\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(\n    checkInterval: number,\n    checkSession: SessionCheckCallback,\n    onSessionExpired: SessionExpiredCallback,\n  ) {\n    this.checkInterval = checkInterval;\n    this.checkSession = checkSession;\n    this.onSessionExpired = onSessionExpired;\n  }\n\n  /**\n   * Handles the session expiration when it is about to expire soon.\n   * Stops any ongoing checks and schedules a timeout for the expiration.\n   *\n   * @param {number} timeToExpiration - The time in milliseconds until the session expires.\n   */\n  scheduleSessionExpiry(timeToExpiration: number): void {\n    this.stop();\n    this.timeoutID = setTimeout(async () => {\n      this.stop();\n      this.onSessionExpired();\n    }, timeToExpiration);\n  }\n\n  /**\n   * Starts the session check process.\n   * Determines when the next check should run based on the last known check time and session expiration.\n   * If the session is expiring soon, schedules an expiration event instead of starting periodic checks.\n   *\n   * @param {number} lastCheck - The timestamp (in milliseconds) of the last session check.\n   * @param {number} expiration - The timestamp (in milliseconds) of when the session expires.\n   */\n  start(lastCheck: number = 0, expiration: number = 0): void {\n    const timeToNextCheck = this.calcTimeToNextCheck(lastCheck);\n\n    if (this.sessionExpiresSoon(expiration)) {\n      this.scheduleSessionExpiry(timeToNextCheck);\n      return;\n    }\n\n    // Schedule the first check after an optional delay\n    this.timeoutID = setTimeout(async () => {\n      try {\n        let result = await this.checkSession();\n\n        if (result.is_valid) {\n          if (this.sessionExpiresSoon(result.expiration)) {\n            this.scheduleSessionExpiry(result.expiration - Date.now());\n            return;\n          }\n\n          // Begin periodic checks\n          this.intervalID = setInterval(async () => {\n            result = await this.checkSession();\n\n            if (result.is_valid) {\n              if (this.sessionExpiresSoon(result.expiration)) {\n                this.scheduleSessionExpiry(result.expiration - Date.now());\n              }\n            } else {\n              this.stop();\n            }\n          }, this.checkInterval);\n        } else {\n          this.stop();\n        }\n      } catch (e) {\n        console.log(e);\n      }\n    }, timeToNextCheck);\n  }\n\n  /**\n   * Stops the session check process and clears all timers.\n   */\n  stop(): void {\n    if (this.timeoutID) {\n      clearTimeout(this.timeoutID);\n      this.timeoutID = null;\n    }\n\n    if (this.intervalID) {\n      clearInterval(this.intervalID);\n      this.intervalID = null;\n    }\n  }\n\n  /**\n   * Checks if the scheduler is currently running.\n   * @returns {boolean} True if the scheduler is running; otherwise, false.\n   */\n  isRunning(): boolean {\n    return this.timeoutID !== null || this.intervalID !== null;\n  }\n\n  /**\n   * Checks if the session is about to expire.\n   * @param {number} expiration - Timestamp when the session will expire.\n   * @returns {boolean} True if the session is about to expire; otherwise, false.\n   */\n  sessionExpiresSoon(expiration: number): boolean {\n    return expiration > 0 && expiration - Date.now() <= this.checkInterval;\n  }\n\n  /**\n   * Calculates the time until the next session check should occur.\n   *\n   * @param {number} lastCheck - The timestamp (in milliseconds) of the last session check.\n   * @returns {number} The time in milliseconds until the next check should be performed.\n   */\n  calcTimeToNextCheck(lastCheck: number): number {\n    const timeSinceLastCheck = Date.now() - lastCheck;\n    return this.checkInterval >= timeSinceLastCheck\n      ? this.checkInterval - (timeSinceLastCheck % this.checkInterval)\n      : 0;\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/SessionChannel.ts",
    "content": "import { Claims } from \"../Dto\";\n\n/**\n * Enum-like type defining the actions that can be broadcasted.\n *\n * @ignore\n * @category SDK\n * @subcategory Internal\n */\ntype Action = \"sessionExpired\" | \"sessionCreated\" | \"requestLeadership\";\n\n/**\n * Interface representing the data structure of a channel event.\n *\n * @interface\n * @property {Action} action - The type of action being broadcasted.\n * @property {Claims=} claims - Optional claims associated with the event.\n * @property {boolean=} is_valid - Optional indication of the session validity.\n * @category SDK\n * @subcategory Internal\n */\nexport interface BroadcastMessage {\n  action: Action;\n  claims?: Claims;\n  is_valid?: boolean;\n}\n\n/**\n * Callback type for handling broadcast messages.\n *\n * @ignore\n */\n// eslint-disable-next-line no-unused-vars\ntype Callback = (msg: BroadcastMessage) => void;\n\n/**\n * Manages inter-tab communication using the BroadcastChannel API.\n *\n * @category SDK\n * @subcategory Internal\n * @param {string} channelName - The name of the broadcast channel.\n * @param {Callback} onSessionExpired - Callback invoked when the session has expired.\n * @param {Callback} onSessionCreated - Callback invoked when a session is created.\n * @param {Callback} onLeadershipRequested - Callback invoked when a leadership request is received.\n */\nexport class SessionChannel {\n  channel: BroadcastChannel; // The broadcast channel used for communication.\n  onSessionExpired: Callback; // Callback invoked when the session has expired.\n  onSessionCreated: Callback; // Callback invoked when a session is created.\n  onLeadershipRequested: Callback; // Callback invoked when a leadership request is received.\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(\n    channelName: string = \"hanko_session\",\n    onSessionExpired: Callback,\n    onSessionCreated: Callback,\n    onLeadershipRequested: Callback,\n  ) {\n    this.onSessionExpired = onSessionExpired;\n    this.onSessionCreated = onSessionCreated;\n    this.onLeadershipRequested = onLeadershipRequested;\n\n    this.channel = new BroadcastChannel(channelName);\n    this.channel.onmessage = this.handleMessage;\n  }\n\n  /**\n   * Sends a message via the broadcast channel to inform other tabs of session changes.\n   *\n   * @param {BroadcastMessage} msg - The messsage to broadcast.\n   */\n  post(msg: BroadcastMessage) {\n    this.channel.postMessage(msg);\n  }\n\n  /**\n   * Handles incoming messages from the broadcast channel.\n   *\n   * @param {MessageEvent} event - The message event containing the broadcast data.\n   * @private\n   */\n  private handleMessage = (event: MessageEvent) => {\n    const data = event.data as BroadcastMessage;\n    switch (data.action) {\n      case \"sessionExpired\":\n        this.onSessionExpired(data);\n        break;\n      case \"sessionCreated\":\n        this.onSessionCreated(data);\n        break;\n      case \"requestLeadership\":\n        this.onLeadershipRequested(data);\n        break;\n    }\n  };\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/SessionState.ts",
    "content": "/**\n * Represents the session state with expiration and last check timestamps.\n *\n * @category SDK\n * @subcategory Internal\n */\nexport interface State {\n  expiration: number; // Timestamp (in milliseconds) when the session expires.\n  lastCheck: number; // Timestamp (in milliseconds) of the last session check.\n}\n\n/**\n * Manages session state persistence using localStorage.\n *\n * @category SDK\n * @subcategory Internal\n */\nexport class SessionState {\n  private readonly storageKey: string;\n  private readonly defaultState: State = {\n    expiration: 0,\n    lastCheck: 0,\n  };\n\n  /**\n   * Creates an instance of SessionState.\n   *\n   * @param {string} storageKey - The key used to store session state in localStorage.\n   */\n  constructor(storageKey: string) {\n    this.storageKey = storageKey;\n  }\n\n  /**\n   * Loads the current session state from localStorage.\n   *\n   * @returns {State} The parsed session state or a default state if not found.\n   */\n  load(): State {\n    const item = window.localStorage.getItem(this.storageKey);\n    return item == null ? this.defaultState : JSON.parse(item);\n  }\n\n  /**\n   * Saves the session state to localStorage.\n   *\n   * @param {State | null} session - The session state to save. If null, the default state is used.\n   */\n  save(session: State | null): void {\n    window.localStorage.setItem(\n      this.storageKey,\n      JSON.stringify(session ? session : this.defaultState),\n    );\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/events/WindowActivityManager.ts",
    "content": "// Callback type for handling window activity changes.\ntype Callback = () => void;\n\n/**\n * Manages window focus and blur events.\n *\n * @class\n * @category SDK\n * @subcategory Internal\n * @param {Callback} onActivityCallback - Callback to invoke when the window gains focus.\n * @param {Callback} onInactivityCallback - Callback to invoke when the window loses focus.\n */\nexport class WindowActivityManager {\n  private readonly onActivityCallback: Callback; // Callback for when the window or tab gains focus.\n  private readonly onInactivityCallback: Callback; // Callback for when the window or tab loses focus.\n\n  // eslint-disable-next-line require-jsdoc\n  constructor(onActivityCallback: Callback, onInactivityCallback: Callback) {\n    this.onActivityCallback = onActivityCallback;\n    this.onInactivityCallback = onInactivityCallback;\n\n    // Attach event listeners for focus and blur\n    window.addEventListener(\"focus\", this.handleFocus);\n    window.addEventListener(\"blur\", this.handleBlur);\n    document.addEventListener(\"visibilitychange\", this.handleVisibilityChange);\n  }\n\n  /**\n   * Handles the focus event and invokes the activity callback.\n   * @private\n   */\n  private handleFocus = (): void => {\n    this.onActivityCallback();\n  };\n\n  /**\n   * Handles the blur event and invokes the inactivity callback.\n   * @private\n   */\n  private handleBlur = (): void => {\n    this.onInactivityCallback();\n  };\n\n  /**\n   * Handles the visibility change event and invokes appropriate callbacks.\n   * @private\n   */\n  private handleVisibilityChange = (): void => {\n    if (document.visibilityState === \"visible\") {\n      this.onActivityCallback();\n    } else {\n      this.onInactivityCallback();\n    }\n  };\n\n  /**\n   * Checks if the current window has focus.\n   * @returns {boolean} True if the window has focus; otherwise, false.\n   */\n  hasFocus = (): boolean => {\n    return document.hasFocus();\n  };\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/State.ts",
    "content": "import { Hanko } from \"../../Hanko\";\nimport { Actions, Payloads, StateName } from \"./types/state\";\nimport { Input } from \"./types/input\";\nimport { FlowError } from \"./types/flowError\";\nimport { Action as ActionType } from \"./types/action\";\nimport { AnyState, FlowName, FlowResponse } from \"./types/flow\";\nimport { autoSteps } from \"./auto-steps\";\nimport { passkeyAutofillActivationHandlers } from \"./passkey-autofill-activation\";\n\nexport type AutoSteppedStates = keyof typeof autoSteps;\n\nexport type PasskeyAutofillStates =\n  keyof typeof passkeyAutofillActivationHandlers;\n\nexport type AutoStepExclusion = AutoSteppedStates[] | \"all\";\n\nexport type ActionMap<TState extends StateName> = {\n  [K in keyof Actions[TState]]: Action<\n    Actions[TState][K] extends ActionType<infer TInputs> ? TInputs : never\n  >;\n};\n\nexport type ActionInfo = {\n  name: string;\n  relatedStateName: StateName;\n};\n\nexport interface StateInitConfig {\n  dispatchAfterStateChangeEvent?: boolean;\n  excludeAutoSteps?: AutoStepExclusion;\n  previousAction?: ActionInfo;\n  isCached?: boolean;\n  cacheKey?: string;\n}\n\nexport type StateCreateConfig = Pick<\n  StateInitConfig,\n  \"dispatchAfterStateChangeEvent\" | \"excludeAutoSteps\" | \"cacheKey\"\n> & {\n  loadFromCache?: boolean;\n};\n\nexport type ActionRunConfig = Pick<\n  StateInitConfig,\n  \"dispatchAfterStateChangeEvent\"\n>;\n\ntype SerializedState = FlowResponse<any> & {\n  flow_name: FlowName;\n  previous_action?: ActionInfo;\n  is_cached?: boolean;\n};\n\ntype ExtractInputValues<TInputs> = {\n  [K in keyof TInputs]: TInputs[K] extends Input<infer TValue> ? TValue : never;\n};\n\n/**\n * Represents a state in a flow with associated actions and properties.\n * @template TState - The specific state name type.\n * @constructor\n * @param {Hanko} hanko - The Hanko instance for API interactions.\n * @param {FlowName} flowName - The name of the flow this state belongs to.\n * @param {FlowResponse<TState>} response - The flow response containing state data.\n * @param {StateInitConfig} [options={}] - Configuration options for state initialization.\n * @category SDK\n * @subcategory FlowAPI\n */\nexport class State<TState extends StateName = StateName> {\n  public readonly name: TState;\n  public readonly flowName: FlowName;\n  public error?: FlowError;\n  public readonly payload?: Payloads[TState];\n  public readonly actions: ActionMap<TState>;\n  public readonly csrfToken: string;\n  public readonly status: number;\n  public readonly previousAction?: ActionInfo;\n  public readonly isCached: boolean;\n  public readonly cacheKey: string;\n  public readonly hanko: Hanko;\n  public invokedAction?: ActionInfo;\n  public readonly excludeAutoSteps: AutoStepExclusion;\n\n  public readonly autoStep?: TState extends AutoSteppedStates\n    ? () => Promise<AnyState>\n    : never;\n  public readonly passkeyAutofillActivation: TState extends PasskeyAutofillStates\n    ? () => Promise<void>\n    : never;\n\n  /**\n   * Constructs a new State instance.\n   * @param {Hanko} hanko - The Hanko instance for API interactions.\n   * @param {FlowName} flowName - The name of the flow this state belongs to.\n   * @param {FlowResponse<TState>} response - The flow response containing state data.\n   * @param {StateInitConfig} [options={}] - Configuration options for state initialization.\n   */\n  constructor(\n    hanko: Hanko,\n    flowName: FlowName,\n    response: FlowResponse<TState>,\n    options: StateInitConfig = {},\n  ) {\n    this.flowName = flowName;\n    this.name = response.name;\n    this.error = response.error;\n    this.payload = response.payload;\n    this.csrfToken = response.csrf_token;\n    this.status = response.status;\n    this.hanko = hanko;\n    this.actions = this.buildActionMap(response.actions);\n\n    if (this.name in autoSteps) {\n      const handler = autoSteps[this.name as AutoSteppedStates];\n      (this.autoStep as () => Promise<AnyState>) = () => handler(this as any);\n    }\n\n    if (this.name in passkeyAutofillActivationHandlers) {\n      const handler =\n        passkeyAutofillActivationHandlers[this.name as PasskeyAutofillStates];\n      (this.passkeyAutofillActivation as () => Promise<void>) = () =>\n        handler(this as any);\n    }\n\n    const {\n      dispatchAfterStateChangeEvent = true,\n      excludeAutoSteps = null,\n      previousAction = null,\n      isCached = false,\n      cacheKey = \"hanko-flow-state\",\n    } = options;\n\n    this.excludeAutoSteps = excludeAutoSteps;\n    this.previousAction = previousAction;\n    this.isCached = isCached;\n    this.cacheKey = cacheKey;\n\n    if (dispatchAfterStateChangeEvent) {\n      this.dispatchAfterStateChangeEvent();\n    }\n  }\n\n  /**\n   * Builds the action map for this state, wrapping it in a Proxy to handle undefined actions.\n   * @param {Actions} actions - The actions available in this state.\n   * @returns {ActionMap<TState>} The action map for the state.\n   * @private\n   */\n  private buildActionMap(actions: Actions[TState]): ActionMap<TState> {\n    const actionMap: Partial<ActionMap<TState>> = {};\n\n    Object.keys(actions).forEach((actionName) => {\n      const key = actionName as keyof Actions[TState];\n      const action = actions[key] as ActionType<any>;\n\n      actionMap[key] = new Action(action, this);\n    });\n\n    // Return a Proxy that handles missing keys\n    return new Proxy(actionMap as ActionMap<TState>, {\n      get: (target: ActionMap<TState>, prop: string | symbol): Action<any> => {\n        if (prop in target) {\n          return target[prop as keyof ActionMap<TState>];\n        }\n\n        const actionName = typeof prop === \"string\" ? prop : prop.toString();\n\n        return Action.createDisabled(actionName, this);\n      },\n    });\n  }\n\n  /**\n   * Dispatches an event after the state has changed.\n   */\n  public dispatchAfterStateChangeEvent() {\n    this.hanko.relay.dispatchAfterStateChangeEvent({\n      state: this as AnyState,\n    });\n  }\n\n  /**\n   * Serializes the current state into a storable format.\n   * @returns {SerializedState} The serialized state object.\n   */\n  public serialize(): SerializedState {\n    return {\n      flow_name: this.flowName,\n      name: this.name,\n      error: this.error,\n      payload: this.payload,\n      csrf_token: this.csrfToken,\n      status: this.status,\n      previous_action: this.previousAction,\n      actions: Object.fromEntries(\n        (Object.entries(this.actions) as [string, Action<any>][]).map(\n          ([name, action]) => [\n            name,\n            {\n              action: action.name,\n              href: action.href,\n              inputs: action.inputs,\n              description: null,\n            },\n          ],\n        ),\n      ),\n    };\n  }\n\n  /**\n   * Saves the current state to localStorage.\n   * @returns {void}\n   */\n  public saveToLocalStorage(): void {\n    localStorage.setItem(\n      this.cacheKey,\n      JSON.stringify({ ...this.serialize(), is_cached: true }),\n    );\n  }\n\n  /**\n   * Removes the current state from localStorage.\n   * @returns {void}\n   */\n  public removeFromLocalStorage(): void {\n    localStorage.removeItem(this.cacheKey);\n  }\n\n  /**\n   * Initializes a flow state, processing auto-steps if applicable.\n   * @param {Hanko} hanko - The Hanko instance for API interactions.\n   * @param {FlowName} flowName - The name of the flow.\n   * @param {FlowResponse<any>} response - The initial flow response.\n   * @param {StateInitConfig} [options={}] - Configuration options.\n   * @param {boolean} [options.dispatchAfterStateChangeEvent=true] - Whether to dispatch an event after state change.\n   * @param {AutoStepExclusion} [options.excludeAutoSteps=null] - States to exclude from auto-step processing, or \"all\".\n   * @param {ActionInfo} [options.previousAction=null] - Information about the previous action.\n   * @param {boolean} [options.isCached=false] - Whether the state is loaded from cache.\n   * @param {string} [options.cacheKey=\"hanko-flow-state\"] - Key for localStorage caching.\n   * @returns {Promise<AnyState>} A promise resolving to the initialized state.\n   */\n  public static async initializeFlowState(\n    hanko: Hanko,\n    flowName: FlowName,\n    response: FlowResponse<any>,\n    options: StateInitConfig = {},\n  ): Promise<AnyState> {\n    let state = new State(hanko, flowName, response, options);\n\n    if (state.excludeAutoSteps != \"all\") {\n      while (\n        state &&\n        state.autoStep &&\n        !state.excludeAutoSteps?.includes(state.name)\n      ) {\n        const nextState = await state.autoStep();\n        if (nextState.name != state.name) {\n          state = nextState;\n        } else {\n          return nextState;\n        }\n      }\n    }\n\n    return state;\n  }\n\n  /**\n   * Retrieves and parses state data from localStorage.\n   * @param {string} cacheKey - The key used to store the state in localStorage.\n   * @returns {SerializedState | undefined} The parsed serialized state, or undefined if not found or invalid.\n   */\n  public static readFromLocalStorage(\n    cacheKey: string,\n  ): SerializedState | undefined {\n    const raw = localStorage.getItem(cacheKey);\n    if (raw) {\n      try {\n        return JSON.parse(raw) as SerializedState;\n      } catch {\n        return undefined;\n      }\n    }\n  }\n\n  /**\n   * Creates a new state instance, using cached or fetched data.\n   * @param {Hanko} hanko - The Hanko instance for API interactions.\n   * @param {FlowName} flowName - The name of the flow.\n   * @param {StateCreateConfig} [config={}] - Configuration options.\n   * @param {boolean} [config.dispatchAfterStateChangeEvent=true] - Whether to dispatch an event after state change.\n   * @param {AutoStepExclusion} [config.excludeAutoSteps=null] - States to exclude from auto-step processing, or \"all\".\n   * @param {string} [config.cacheKey=\"hanko-flow-state\"] - Key for localStorage caching.\n   * @param {boolean} [config.loadFromCache=true] - Whether to attempt loading from cache.\n   * @returns {Promise<AnyState>} A promise resolving to the created state.\n   */\n  public static async create(\n    hanko: Hanko,\n    flowName: FlowName,\n    config: StateCreateConfig = {},\n  ): Promise<AnyState> {\n    const { cacheKey = \"hanko-flow-state\", loadFromCache = true } = config;\n    if (loadFromCache) {\n      const cachedState = State.readFromLocalStorage(cacheKey);\n      if (cachedState) {\n        return State.deserialize(hanko, cachedState, {\n          ...config,\n          cacheKey,\n        });\n      }\n    }\n\n    const newState = await State.fetchState(hanko, `/${flowName}`);\n    return State.initializeFlowState(hanko, flowName, newState, {\n      ...config,\n      cacheKey,\n    });\n  }\n\n  /**\n   * Deserializes a state from a serialized state object.\n   * @param {Hanko} hanko - The Hanko instance for API interactions.\n   * @param {SerializedState} serializedState - The serialized state data.\n   * @param {StateCreateConfig} [config={}] - Configuration options.\n   * @param {boolean} [config.dispatchAfterStateChangeEvent=true] - Whether to dispatch an event after state change.\n   * @param {AutoStepExclusion} [config.excludeAutoSteps=null] - States to exclude from auto-step processing, or \"all\".\n   * @param {string} [config.cacheKey=\"hanko-flow-state\"] - Key for localStorage caching.\n   * @param {boolean} [config.loadFromCache=true] - Whether to attempt loading from cache.\n   * @returns {Promise<AnyState>} A promise resolving to the deserialized state.\n   */\n  public static async deserialize(\n    hanko: Hanko,\n    serializedState: SerializedState,\n    config: StateCreateConfig = {},\n  ): Promise<AnyState> {\n    return State.initializeFlowState(\n      hanko,\n      serializedState.flow_name,\n      serializedState,\n      {\n        ...config,\n        previousAction: serializedState.previous_action,\n        isCached: serializedState.is_cached,\n      },\n    );\n  }\n\n  /**\n   * Fetches state data from the server.\n   * @param {Hanko} hanko - The Hanko instance for API interactions.\n   * @param {string} href - The endpoint to fetch from.\n   * @param {any} [body] - Optional request body.\n   * @returns {Promise<FlowResponse<any>>} A promise resolving to the flow response.\n   */\n  static async fetchState(\n    hanko: Hanko,\n    href: string,\n    body?: any,\n  ): Promise<FlowResponse<any>> {\n    try {\n      const response = await hanko.client.post(href, body);\n      return response.json();\n    } catch (error) {\n      return State.createErrorResponse(error);\n    }\n  }\n\n  /**\n   * Creates an error flow response.\n   * @param {FlowError} error - The error to include in the response.\n   * @returns {FlowResponse<\"error\">} A flow response with error details.\n   * @private\n   */\n  private static createErrorResponse(error: FlowError): FlowResponse<\"error\"> {\n    return {\n      actions: null,\n      csrf_token: \"\",\n      name: \"error\",\n      payload: null,\n      status: 0,\n      error,\n    };\n  }\n}\n\n/**\n * Represents an actionable operation within a state.\n * @template TInputs - The type of inputs required for the action.\n * @param {ActionType<TInputs>} action - The action type definition.\n * @param {State} parentState - The state this action belongs to.\n * @param {boolean} [enabled=true] - Whether the action is enabled.\n * @category SDK\n * @subcategory FlowAPI\n */\nexport class Action<TInputs> {\n  public readonly enabled: boolean;\n  public readonly href: string;\n  public readonly name: string;\n  public readonly inputs: TInputs;\n  private readonly parentState: State;\n\n  /**\n   * Constructs a new Action instance.\n   * @param {ActionType<TInputs>} action - The action type definition.\n   * @param {State} parentState - The state this action belongs to.\n   * @param {boolean} [enabled=true] - Whether the action is enabled.\n   */\n  constructor(\n    action: ActionType<TInputs>,\n    parentState: State,\n    enabled: boolean = true,\n  ) {\n    this.enabled = enabled;\n    this.href = action.href;\n    this.name = action.action;\n    this.inputs = action.inputs;\n    this.parentState = parentState;\n  }\n\n  /**\n   * Creates a disabled action instance.\n   * @template TInputs - The type of inputs (inferred as empty).\n   * @param {string} name - The name of the action.\n   * @param {State} parentState - The state this action belongs to.\n   * @returns {Action<TInputs>} A disabled action instance.\n   */\n  static createDisabled<TInputs>(\n    name: string,\n    parentState: State,\n  ): Action<TInputs> {\n    return new Action(\n      {\n        action: name,\n        href: \"\", // No valid href since it’s disabled\n        inputs: {} as TInputs,\n        description: \"Disabled action\",\n      },\n      parentState,\n      false,\n    );\n  }\n\n  /**\n   * Executes the action, transitioning to a new state.\n   * @param {ExtractInputValues<TInputs>} [inputValues=null] - Values for the action's inputs.\n   * @param {ActionRunConfig} [config={}] - Configuration options.\n   * @param {boolean} [config.dispatchAfterStateChangeEvent=true] - Whether to dispatch an event after state change.\n   * @returns {Promise<AnyState>} A promise resolving to the next state.\n   * @throws {FlowError} If the action is disabled or already invoked.\n   */\n  async run(\n    inputValues: ExtractInputValues<TInputs> = null,\n    config: ActionRunConfig = {},\n  ): Promise<AnyState> {\n    const {\n      name,\n      hanko,\n      flowName,\n      csrfToken,\n      invokedAction,\n      excludeAutoSteps,\n      cacheKey,\n    } = this.parentState;\n    const { dispatchAfterStateChangeEvent = true } = config;\n\n    if (!this.enabled) {\n      throw new Error(\n        `Action '${this.name}' is not enabled in state '${name}'`,\n      );\n    }\n\n    if (invokedAction) {\n      throw new Error(\n        `An action '${invokedAction.name}' has already been invoked on state '${invokedAction.relatedStateName}'. No further actions can be run.`,\n      );\n    }\n\n    this.parentState.invokedAction = {\n      name: this.name,\n      relatedStateName: name,\n    };\n\n    hanko.relay.dispatchBeforeStateChangeEvent({\n      state: this.parentState as AnyState,\n    });\n\n    // Extract default values from this.inputs\n    const defaultValues = Object.keys(this.inputs).reduce(\n      (acc, key) => {\n        const input = (this.inputs as any)[key] as Input<any>;\n        if (input.value !== undefined) {\n          acc[key] = input.value;\n        }\n        return acc;\n      },\n      {} as Record<string, any>,\n    );\n\n    // Merge defaults with user-provided inputs\n    const mergedInputData = {\n      ...defaultValues,\n      ...inputValues,\n    };\n\n    const requestBody = {\n      input_data: mergedInputData,\n      csrf_token: csrfToken,\n    };\n\n    const response = await State.fetchState(hanko, this.href, requestBody);\n\n    this.parentState.removeFromLocalStorage();\n\n    return State.initializeFlowState(hanko, flowName, response, {\n      dispatchAfterStateChangeEvent,\n      excludeAutoSteps,\n      previousAction: invokedAction,\n      cacheKey,\n    });\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/WebauthnManager.ts",
    "content": "import {\n  CredentialRequestOptionsJSON,\n  CredentialCreationOptionsJSON,\n  PublicKeyCredentialWithAssertionJSON,\n  PublicKeyCredentialWithAttestationJSON,\n  create,\n  get,\n} from \"@github/webauthn-json\";\n\n/**\n * Manages WebAuthn credential operations as a singleton, ensuring only one active request at a time.\n * Uses an internal AbortController to cancel previous requests when a new one is initiated.\n */\nclass WebauthnManager {\n  private static instance: WebauthnManager | null = null;\n  private abortController = new AbortController();\n  // eslint-disable-next-line no-useless-constructor,require-jsdoc\n  private constructor() {}\n\n  /**\n   * Gets the singleton instance of WebauthnManager.\n   * Creates a new instance if one doesn't exist, otherwise returns the existing one.\n   * @returns {WebauthnManager} The singleton instance\n   */\n  public static getInstance(): WebauthnManager {\n    if (!WebauthnManager.instance) {\n      WebauthnManager.instance = new WebauthnManager();\n    }\n    return WebauthnManager.instance;\n  }\n\n  /**\n   * Creates a new abort signal, aborting any ongoing WebAuthn request.\n   * @private\n   * @returns {AbortSignal} The new abort signal\n   */\n  private createAbortSignal(): AbortSignal {\n    this.abortController.abort(); // Cancel any ongoing request\n    this.abortController = new AbortController();\n    return this.abortController.signal;\n  }\n\n  /**\n   * Retrieves a WebAuthn credential using the provided options.\n   * Aborts any previous request before starting a new one.\n   * @param {CredentialRequestOptionsJSON} options - The options for credential retrieval\n   * @returns {Promise<PublicKeyCredentialWithAssertionJSON>} A promise resolving to the retrieved credential\n   * @throws {DOMException} If the WebAuthn request fails (e.g., aborted, not allowed)\n   */\n  public async getWebauthnCredential(\n    options: CredentialRequestOptionsJSON,\n  ): Promise<PublicKeyCredentialWithAssertionJSON> {\n    return await get({\n      ...options,\n      signal: this.createAbortSignal(),\n    });\n  }\n\n  /**\n   * Retrieves a WebAuthn credential with conditional UI mediation.\n   * Aborts any previous request before starting a new one.\n   * @param {CredentialRequestOptionsJSON} publicKey - The public key options for conditional retrieval\n   * @returns {Promise<PublicKeyCredentialWithAssertionJSON>} A promise resolving to the retrieved credential\n   * @throws {DOMException} If the WebAuthn request fails (e.g., aborted, not allowed)\n   */\n  public async getConditionalWebauthnCredential(\n    publicKey: CredentialRequestOptionsJSON[\"publicKey\"],\n  ): Promise<PublicKeyCredentialWithAssertionJSON> {\n    return await get({\n      publicKey,\n      mediation: \"conditional\" as CredentialMediationRequirement,\n      signal: this.createAbortSignal(),\n    });\n  }\n\n  /**\n   * Creates a new WebAuthn credential using the provided options.\n   * Aborts any previous request before starting a new one.\n   * @param {CredentialCreationOptionsJSON} options - The options for credential creation\n   * @returns {Promise<PublicKeyCredentialWithAttestationJSON>} A promise resolving to the created credential\n   * @throws {DOMException} If the WebAuthn request fails (e.g., aborted, not allowed)\n   */\n  public async createWebauthnCredential(\n    options: CredentialCreationOptionsJSON,\n  ): Promise<PublicKeyCredentialWithAttestationJSON> {\n    return await create({\n      ...options,\n      signal: this.createAbortSignal(),\n    });\n  }\n}\n\nexport default WebauthnManager;\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/auto-steps.ts",
    "content": "import { AutoSteps } from \"./types/flow\";\nimport { WebauthnSupport } from \"../WebauthnSupport\";\nimport WebauthnManager from \"./WebauthnManager\";\nimport { CredentialCreationOptionsJSON } from \"@github/webauthn-json\";\nimport { clearStoredCodeVerifier, getStoredCodeVerifier } from \"../Pkce\";\n\n// Helper function to handle WebAuthn credential creation and error handling\n// eslint-disable-next-line require-jsdoc\nasync function handleCredentialCreation(\n  state: any,\n  manager: WebauthnManager,\n  options: CredentialCreationOptionsJSON,\n  errorCode: string = \"webauthn_credential_already_exists\",\n  errorMessage: string = \"Webauthn credential already exists\",\n) {\n  try {\n    const attestationResponse = await manager.createWebauthnCredential(options);\n    return await state.actions.webauthn_verify_attestation_response.run({\n      public_key: attestationResponse,\n    });\n  } catch {\n    const nextState = await state.actions.back.run();\n    nextState.error = { code: errorCode, message: errorMessage };\n    return nextState;\n  }\n}\n\nexport const autoSteps: AutoSteps = {\n  preflight: async (state) => {\n    return await state.actions.register_client_capabilities.run({\n      webauthn_available: WebauthnSupport.supported(),\n      webauthn_conditional_mediation_available:\n        await WebauthnSupport.isConditionalMediationAvailable(),\n      webauthn_platform_authenticator_available:\n        await WebauthnSupport.isPlatformAuthenticatorAvailable(),\n    });\n  },\n\n  login_passkey: async (state) => {\n    const manager = WebauthnManager.getInstance();\n    try {\n      const assertionResponse = await manager.getWebauthnCredential(\n        state.payload.request_options,\n      );\n      return await state.actions.webauthn_verify_assertion_response.run({\n        assertion_response: assertionResponse,\n      });\n    } catch {\n      const nextState = await state.actions.back.run();\n      if (state.error) {\n        nextState.error = state.error;\n      }\n      return nextState;\n    }\n  },\n\n  onboarding_verify_passkey_attestation: async (state) => {\n    const manager = WebauthnManager.getInstance();\n    return handleCredentialCreation(\n      state,\n      manager,\n      state.payload.creation_options,\n    );\n  },\n\n  webauthn_credential_verification: async (state) => {\n    const manager = WebauthnManager.getInstance();\n    return handleCredentialCreation(\n      state,\n      manager,\n      state.payload.creation_options,\n    );\n  },\n\n  async thirdparty(state) {\n    const searchParams = new URLSearchParams(window.location.search);\n    const token = searchParams.get(\"hanko_token\");\n    const error = searchParams.get(\"error\");\n\n    const updateUrl = (paramsToDelete: string[]) => {\n      paramsToDelete.forEach((param) => searchParams.delete(param));\n      const newSearch = searchParams.toString()\n        ? `?${searchParams.toString()}`\n        : \"\";\n      history.replaceState(\n        null,\n        null,\n        `${window.location.pathname}${newSearch}`,\n      );\n    };\n\n    if (token?.length > 0) {\n      updateUrl([\"hanko_token\"]);\n      const verifier = getStoredCodeVerifier();\n\n      try {\n        return await state.actions.exchange_token.run({\n          token,\n          code_verifier: verifier || undefined,\n        });\n      } finally {\n        clearStoredCodeVerifier();\n      }\n    }\n\n    if (error?.length > 0) {\n      const errorCode =\n        error === \"access_denied\"\n          ? \"third_party_access_denied\"\n          : \"technical_error\";\n      const message = searchParams.get(\"error_description\");\n\n      updateUrl([\"error\", \"error_description\"]);\n\n      const nextState = await state.actions.back.run(null, {\n        dispatchAfterStateChangeEvent: false,\n      });\n\n      nextState.error = { code: errorCode, message };\n      nextState.dispatchAfterStateChangeEvent();\n\n      return nextState;\n    }\n\n    if (!state.isCached) {\n      state.saveToLocalStorage();\n      window.location.assign(state.payload.redirect_url);\n    } else {\n      return await state.actions.back.run();\n    }\n\n    return state;\n  },\n\n  success: async (state) => {\n    const { claims } = state.payload;\n    const expirationSeconds = Date.parse(claims.expiration) - Date.now();\n    state.removeFromLocalStorage();\n    state.hanko.relay.dispatchSessionCreatedEvent({\n      claims,\n      expirationSeconds,\n    });\n    return state;\n  },\n\n  account_deleted: async (state) => {\n    state.removeFromLocalStorage();\n    state.hanko.relay.dispatchUserDeletedEvent();\n    return state;\n  },\n};\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/passkey-autofill-activation.ts",
    "content": "import { PasskeyAutofillActivationHandlers } from \"./types/flow\";\nimport WebauthnManager from \"./WebauthnManager\";\n\nexport const passkeyAutofillActivationHandlers: PasskeyAutofillActivationHandlers =\n  {\n    login_init: async (state) => {\n      return void (async function () {\n        const manager = WebauthnManager.getInstance();\n\n        if (state.payload.request_options) {\n          try {\n            const { publicKey } = state.payload.request_options;\n\n            const assertionResponse =\n              await manager.getConditionalWebauthnCredential(publicKey);\n\n            return await state.actions.webauthn_verify_assertion_response.run({\n              assertion_response: assertionResponse,\n            });\n          } catch {\n            // We do not need to handle the error, because this is a conditional request, which can fail silently\n            return;\n          }\n        }\n      })();\n    },\n  };\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/types/action.ts",
    "content": "import {\n  ContinueWithLoginIdentifierInputs,\n  EmailCreateInputs,\n  EmailDeleteInputs,\n  EmailSetPrimaryInputs,\n  EmailVerifyInputs,\n  ExchangeTokenInputs,\n  PasskeyCredentialDeleteInputs,\n  PasskeyCredentialRenameInputs,\n  PasswordRecoveryInputs,\n  PasswordInputs,\n  PatchMetadataInputs,\n  RegisterClientCapabilitiesInputs,\n  RegisterLoginIdentifierInputs,\n  RegisterPasswordInputs,\n  ThirdpartyOauthInputs,\n  UsernameSetInputs,\n  VerifyPasscodeInputs,\n  WebauthnVerifyAssertionResponseInputs,\n  WebauthnVerifyAttestationResponseInputs,\n  SessionDeleteInputs,\n  OTPCodeInputs,\n  SecurityKeyDeleteInputs,\n  RememberMeInputs,\n  DisconnectThirdpartyInputs,\n} from \"./input\";\n\nexport interface Action<TInputs> {\n  action: string;\n  href: string;\n  inputs: TInputs;\n  description: string;\n}\n\nexport interface PreflightActions {\n  readonly register_client_capabilities: Action<RegisterClientCapabilitiesInputs>;\n}\n\nexport interface LoginInitActions {\n  readonly continue_with_login_identifier?: Action<ContinueWithLoginIdentifierInputs>;\n  readonly webauthn_generate_request_options?: Action<null>;\n  readonly webauthn_verify_assertion_response?: Action<WebauthnVerifyAssertionResponseInputs>;\n  readonly thirdparty_oauth?: Action<ThirdpartyOauthInputs>;\n  readonly remember_me?: Action<RememberMeInputs>;\n}\n\nexport interface ProfileInitActions {\n  readonly account_delete?: Action<null>;\n  readonly continue_to_otp_secret_creation?: Action<null>;\n  readonly email_create?: Action<EmailCreateInputs>;\n  readonly email_delete?: Action<EmailDeleteInputs>;\n  readonly email_verify?: Action<EmailVerifyInputs>;\n  readonly email_set_primary?: Action<EmailSetPrimaryInputs>;\n  readonly otp_secret_delete?: Action<null>;\n  readonly password_create?: Action<PasswordInputs>;\n  readonly password_update?: Action<PasswordInputs>;\n  readonly password_delete?: Action<null>;\n  readonly patch_metadata: Action<PatchMetadataInputs>;\n  readonly security_key_create?: Action<null>;\n  readonly security_key_delete?: Action<SecurityKeyDeleteInputs>;\n  readonly username_create?: Action<UsernameSetInputs>;\n  readonly username_delete?: Action<null>;\n  readonly username_update?: Action<UsernameSetInputs>;\n  readonly webauthn_credential_create?: Action<null>;\n  readonly webauthn_credential_rename?: Action<PasskeyCredentialRenameInputs>;\n  readonly webauthn_credential_delete?: Action<PasskeyCredentialDeleteInputs>;\n  readonly webauthn_verify_attestation_response?: Action<WebauthnVerifyAttestationResponseInputs>;\n  readonly session_delete?: Action<SessionDeleteInputs>;\n  readonly connect_thirdparty_oauth_provider?: Action<ThirdpartyOauthInputs>;\n  readonly disconnect_thirdparty_oauth_provider?: Action<DisconnectThirdpartyInputs>;\n}\n\nexport interface LoginMethodChooserActions {\n  readonly continue_to_password_login?: Action<null>;\n  readonly continue_to_passcode_confirmation?: Action<null>;\n  readonly webauthn_generate_request_options?: Action<null>;\n  readonly back: Action<null>;\n}\n\nexport interface LoginOTPActions {\n  readonly otp_code_validate: Action<OTPCodeInputs>;\n  readonly continue_to_login_security_key?: Action<null>;\n}\n\nexport interface LoginPasswordActions {\n  readonly password_login: Action<PasswordInputs>;\n  readonly continue_to_passcode_confirmation_recovery?: Action<null>;\n  readonly continue_to_login_method_chooser: Action<null>;\n  readonly back: Action<null>;\n}\n\nexport interface LoginPasswordRecoveryActions {\n  readonly password_recovery: Action<PasswordRecoveryInputs>;\n}\n\nexport interface LoginPasskeyActions {\n  readonly webauthn_verify_assertion_response: Action<WebauthnVerifyAssertionResponseInputs>;\n  readonly back: Action<null>;\n}\n\nexport interface LoginSecurityKeyActions {\n  readonly webauthn_generate_request_options: Action<null>;\n  readonly continue_to_login_otp?: Action<null>;\n}\n\nexport interface MFAMethodChooserActions {\n  readonly continue_to_otp_secret_creation?: Action<null>;\n  readonly continue_to_security_key_creation?: Action<null>;\n  readonly skip?: Action<null>;\n  readonly back?: Action<null>;\n}\n\nexport interface MFAAOTPSecretCreationActions {\n  readonly otp_code_verify: Action<OTPCodeInputs>;\n  readonly back: Action<null>;\n}\n\nexport interface MFASecurityKeyCreationActions {\n  readonly webauthn_generate_creation_options: Action<null>;\n  readonly back: Action<null>;\n}\n\nexport interface OnboardingCreatePasskeyActions {\n  readonly webauthn_generate_creation_options: Action<null>;\n  readonly skip?: Action<null>;\n  readonly back?: Action<null>;\n}\n\nexport interface OnboardingVerifyPasskeyAttestationActions {\n  readonly webauthn_verify_attestation_response: Action<WebauthnVerifyAttestationResponseInputs>;\n  readonly back: Action<null>;\n}\n\nexport interface RegistrationInitActions {\n  readonly register_login_identifier: Action<RegisterLoginIdentifierInputs>;\n  readonly thirdparty_oauth?: Action<ThirdpartyOauthInputs>;\n  readonly remember_me?: Action<RememberMeInputs>;\n}\n\nexport interface PasswordCreationActions {\n  readonly register_password: Action<RegisterPasswordInputs>;\n  readonly back?: Action<null>;\n  readonly skip?: Action<null>;\n}\n\nexport interface PasscodeConfirmationActions {\n  readonly verify_passcode: Action<VerifyPasscodeInputs>;\n  readonly resend_passcode: Action<null>;\n  readonly back: Action<null>;\n}\n\nexport interface OnboardingEmailActions {\n  readonly email_address_set: Action<EmailCreateInputs>;\n  readonly skip: Action<null>;\n}\n\nexport interface OnboardingUsernameActions {\n  readonly username_create: Action<UsernameSetInputs>;\n  readonly skip: Action<null>;\n}\n\nexport interface CredentialOnboardingChooserActions {\n  readonly continue_to_passkey_registration: Action<null>;\n  readonly continue_to_password_registration: Action<null>;\n  readonly skip: Action<null>;\n  readonly back: Action<null>;\n}\n\nexport interface DeviceTrustActions {\n  readonly trust_device: Action<null>;\n  readonly skip: Action<null>;\n  readonly back?: Action<null>;\n}\n\nexport interface ThirdPartyActions {\n  readonly exchange_token: Action<ExchangeTokenInputs>;\n  readonly back?: Action<null>;\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/types/flow.ts",
    "content": "import { StateName, Actions, Payloads } from \"./state\";\nimport { FlowError } from \"./flowError\";\nimport { State } from \"../State\";\n\ntype PickStates<TState extends StateName> = TState;\n\nexport type FlowName = \"login\" | \"registration\" | \"profile\" | \"token_exchange\";\n\nexport type AnyState = { [TState in StateName]: State<TState> }[StateName];\n\nexport type AutoStep<TState extends StateName> = (\n  // eslint-disable-next-line no-unused-vars\n  state: State<TState>,\n) => Promise<AnyState>;\n\nexport type AutoSteps = {\n  [TState in PickStates<\n    | \"preflight\"\n    | \"login_passkey\"\n    | \"onboarding_verify_passkey_attestation\"\n    | \"webauthn_credential_verification\"\n    | \"thirdparty\"\n    | \"success\"\n    | \"account_deleted\"\n  >]: AutoStep<TState>;\n};\n\nexport type PasskeyAutofillActivationHandler<TState extends StateName> = (\n  // eslint-disable-next-line no-unused-vars\n  state: State<TState>,\n) => Promise<void>;\n\nexport type PasskeyAutofillActivationHandlers = {\n  [TState in PickStates<\"login_init\">]: PasskeyAutofillActivationHandler<TState>;\n};\n\nexport interface FlowResponse<TState extends StateName> {\n  name: TState;\n  status: number;\n  payload?: Payloads[TState];\n  actions?: Actions[TState];\n  csrf_token: string;\n  error?: FlowError;\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/types/flowError.ts",
    "content": "export interface FlowError {\n  code: string;\n  message: string;\n  cause?: string;\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/types/input.ts",
    "content": "import { FlowError } from \"./flowError\";\nimport {\n  PublicKeyCredentialWithAssertionJSON,\n  PublicKeyCredentialWithAttestationJSON,\n} from \"@github/webauthn-json\";\n\nexport interface Input<TValue> {\n  readonly name: string;\n  readonly type: string;\n  readonly value?: TValue;\n  readonly min_length?: number;\n  readonly max_length?: number;\n  readonly required?: boolean;\n  readonly hidden?: boolean;\n  readonly error?: FlowError;\n  readonly allowed_values?: AllowedInputValues[];\n}\n\nexport interface AllowedInputValues {\n  readonly name: string;\n  readonly value: string;\n}\n\nexport interface PasswordRecoveryInputs {\n  readonly new_password: Input<string>;\n}\n\nexport interface WebauthnVerifyAssertionResponseInputs {\n  readonly assertion_response: Input<PublicKeyCredentialWithAssertionJSON>;\n}\n\nexport interface WebauthnVerifyAttestationResponseInputs {\n  readonly public_key: Input<PublicKeyCredentialWithAttestationJSON>;\n}\n\nexport interface RegisterLoginIdentifierInputs {\n  readonly email?: Input<string>;\n  readonly username?: Input<string>;\n}\n\nexport interface RegisterPasswordInputs {\n  readonly new_password: Input<string>;\n}\n\nexport interface RegisterClientCapabilitiesInputs {\n  readonly webauthn_available: Input<boolean>;\n  readonly webauthn_conditional_mediation_available: Input<boolean>;\n  readonly webauthn_platform_authenticator_available: Input<boolean>;\n}\n\nexport interface ContinueWithLoginIdentifierInputs {\n  readonly identifier?: Input<string>;\n  readonly email?: Input<string>;\n  readonly username?: Input<string>;\n}\n\nexport interface VerifyPasscodeInputs {\n  readonly code: Input<string>;\n}\n\nexport interface EmailCreateInputs {\n  readonly email: Input<string>;\n}\n\nexport interface EmailDeleteInputs {\n  readonly email_id: Input<string>;\n}\n\nexport interface EmailSetPrimaryInputs {\n  readonly email_id: Input<string>;\n}\n\nexport interface EmailVerifyInputs {\n  readonly email_id: Input<string>;\n}\n\nexport interface PasswordInputs {\n  readonly password: Input<string>;\n}\n\nexport interface PatchMetadataInputs {\n  readonly patch_metadata: Input<Record<string, any>>;\n}\n\nexport interface UsernameSetInputs {\n  readonly username: Input<string>;\n}\n\nexport interface SecurityKeyDeleteInputs {\n  readonly security_key_id: Input<string>;\n}\n\nexport interface PasskeyCredentialRenameInputs {\n  readonly passkey_id: Input<string>;\n  readonly passkey_name: Input<string>;\n}\n\nexport interface PasskeyCredentialDeleteInputs {\n  readonly passkey_id: Input<string>;\n}\n\nexport interface ExchangeTokenInputs {\n  readonly token: Input<string>;\n  readonly code_verifier?: Input<string>;\n}\n\nexport interface ThirdpartyOauthInputs {\n  readonly provider: Input<string>;\n  readonly redirect_to: Input<string>;\n  readonly code_verifier?: Input<string>;\n}\n\nexport interface DisconnectThirdpartyInputs {\n  readonly identity_id: Input<string>;\n}\n\nexport interface RememberMeInputs {\n  readonly remember_me: Input<boolean>;\n}\n\nexport interface SessionDeleteInputs {\n  readonly session_id: Input<string>;\n}\n\nexport interface OTPCodeInputs {\n  readonly otp_code: Input<string>;\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/types/payload.ts",
    "content": "import {\n  CredentialCreationOptionsJSON,\n  CredentialRequestOptionsJSON,\n} from \"@github/webauthn-json/src/webauthn-json/basic/json\";\nimport { Claims } from \"../../Dto\";\n\nexport interface PasscodeConfirmationPayload {\n  readonly passcode_resent: boolean;\n  readonly resend_after: number;\n}\n\nexport interface LoginPasskeyPayload {\n  readonly request_options: CredentialRequestOptionsJSON;\n}\n\nexport interface MFAOTPSecretCreationPayload {\n  readonly otp_secret: string;\n  readonly otp_image_source: string;\n}\n\nexport interface OnboardingVerifyPasskeyAttestationPayload {\n  readonly creation_options: CredentialCreationOptionsJSON;\n}\n\nexport interface LoginInitPayload {\n  readonly request_options?: CredentialRequestOptionsJSON;\n}\n\nexport interface WebauthnCredential {\n  readonly id: string;\n  readonly name?: string;\n  readonly public_key: string;\n  readonly attestation_type: string;\n  readonly aaguid: string;\n  readonly last_used_at?: string;\n  readonly created_at: string;\n  readonly transports: string;\n  readonly backup_eligible: string;\n  readonly backup_state: string;\n}\n\nexport interface Username {\n  id: string;\n  username: string;\n  created_at: string;\n  updated_at: string;\n}\n\nexport interface Identity {\n  readonly id: string;\n  readonly provider: string;\n  readonly identity_id?: string;\n}\n\nexport interface Email {\n  readonly id: string;\n  readonly address: string;\n  readonly is_verified: boolean;\n  readonly is_primary: boolean;\n  readonly identities?: Identity[];\n}\n\nexport interface MFAConfig {\n  readonly auth_app_set_up: boolean;\n  readonly totp_enabled: boolean;\n  readonly security_keys_enabled: boolean;\n}\n\nexport type UserMetadata<\n  PublicMetadata extends Record<string, any> = {},\n  UnsafeMetadata extends Record<string, any> = {},\n> = {\n  public_metadata?: PublicMetadata;\n  unsafe_metadata?: UnsafeMetadata;\n};\n\nexport interface User {\n  readonly user_id: string;\n  readonly passkeys?: WebauthnCredential[];\n  readonly security_keys?: WebauthnCredential[];\n  readonly mfa_config?: MFAConfig;\n  readonly emails?: Email[];\n  readonly username?: Username;\n  readonly metadata?: UserMetadata;\n  readonly identities?: Identity[];\n  readonly created_at: string;\n  readonly updated_at: string;\n  readonly name?: string;\n  readonly given_name?: string;\n  readonly family_name?: string;\n  readonly picture?: string;\n}\n\nexport interface Session {\n  readonly id: string;\n  readonly user_agent?: string;\n  readonly user_agent_raw?: string;\n  readonly ip_address?: string;\n  readonly created_at: string;\n  readonly last_used: string;\n  readonly current: boolean;\n}\n\nexport interface ProfilePayload {\n  readonly user: User;\n  readonly sessions?: Session[];\n}\n\nexport type LoginMethod = \"password\" | \"passkey\" | \"passcode\" | \"third_party\";\n\nexport type MFAMethod = \"totp\" | \"security_key\";\n\nexport interface LastLogin {\n  readonly login_method: LoginMethod;\n  readonly mfa_method?: MFAMethod;\n  readonly third_party_provider?: string;\n}\n\nexport interface SuccessPayload {\n  readonly user: User;\n  readonly last_login?: LastLogin;\n  readonly claims: Claims;\n}\n\nexport interface ThirdPartyPayload {\n  readonly redirect_url: string;\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/src/lib/flow-api/types/state.ts",
    "content": "import {\n  CredentialOnboardingChooserActions,\n  DeviceTrustActions,\n  LoginInitActions,\n  LoginMethodChooserActions,\n  LoginOTPActions,\n  LoginPasskeyActions,\n  LoginPasswordActions,\n  LoginPasswordRecoveryActions,\n  LoginSecurityKeyActions,\n  MFAAOTPSecretCreationActions,\n  MFAMethodChooserActions,\n  MFASecurityKeyCreationActions,\n  OnboardingCreatePasskeyActions,\n  OnboardingEmailActions,\n  OnboardingUsernameActions,\n  OnboardingVerifyPasskeyAttestationActions,\n  PasscodeConfirmationActions,\n  PasswordCreationActions,\n  PreflightActions,\n  ProfileInitActions,\n  RegistrationInitActions,\n  ThirdPartyActions,\n} from \"./action\";\n\nimport {\n  LoginInitPayload,\n  LoginPasskeyPayload,\n  MFAOTPSecretCreationPayload,\n  OnboardingVerifyPasskeyAttestationPayload,\n  PasscodeConfirmationPayload,\n  ProfilePayload,\n  SuccessPayload,\n  ThirdPartyPayload,\n} from \"./payload\";\n\nexport type StateName =\n  | \"account_deleted\"\n  | \"credential_onboarding_chooser\"\n  | \"device_trust\"\n  | \"error\"\n  | \"login_init\"\n  | \"login_method_chooser\"\n  | \"login_otp\"\n  | \"login_passkey\"\n  | \"login_password\"\n  | \"login_password_recovery\"\n  | \"login_security_key\"\n  | \"mfa_method_chooser\"\n  | \"mfa_otp_secret_creation\"\n  | \"mfa_security_key_creation\"\n  | \"onboarding_create_passkey\"\n  | \"onboarding_email\"\n  | \"onboarding_username\"\n  | \"onboarding_verify_passkey_attestation\"\n  | \"passcode_confirmation\"\n  | \"password_creation\"\n  | \"preflight\"\n  | \"profile_init\"\n  | \"registration_init\"\n  | \"success\"\n  | \"thirdparty\"\n  | \"webauthn_credential_verification\";\n\nexport interface Actions {\n  readonly account_deleted: null;\n  readonly credential_onboarding_chooser: CredentialOnboardingChooserActions;\n  readonly device_trust: DeviceTrustActions;\n  readonly error: null;\n  readonly login_init: LoginInitActions;\n  readonly login_method_chooser: LoginMethodChooserActions;\n  readonly login_otp: LoginOTPActions;\n  readonly login_passkey: LoginPasskeyActions;\n  readonly login_password: LoginPasswordActions;\n  readonly login_password_recovery: LoginPasswordRecoveryActions;\n  readonly login_security_key: LoginSecurityKeyActions;\n  readonly mfa_method_chooser: MFAMethodChooserActions;\n  readonly mfa_otp_secret_creation: MFAAOTPSecretCreationActions;\n  readonly mfa_security_key_creation: MFASecurityKeyCreationActions;\n  readonly onboarding_create_passkey: OnboardingCreatePasskeyActions;\n  readonly onboarding_email: OnboardingEmailActions;\n  readonly onboarding_username: OnboardingUsernameActions;\n  readonly onboarding_verify_passkey_attestation: OnboardingVerifyPasskeyAttestationActions;\n  readonly passcode_confirmation: PasscodeConfirmationActions;\n  readonly password_creation: PasswordCreationActions;\n  readonly preflight: PreflightActions;\n  readonly profile_init: ProfileInitActions;\n  readonly registration_init: RegistrationInitActions;\n  readonly success: null;\n  readonly thirdparty: ThirdPartyActions;\n  readonly webauthn_credential_verification: OnboardingVerifyPasskeyAttestationActions;\n}\n\nexport interface Payloads {\n  readonly account_deleted: null;\n  readonly credential_onboarding_chooser: null;\n  readonly device_trust: null;\n  readonly error: null;\n  readonly login_init: LoginInitPayload;\n  readonly login_method_chooser: null;\n  readonly login_otp: null;\n  readonly login_passkey: LoginPasskeyPayload;\n  readonly login_password: null;\n  readonly login_password_recovery: null;\n  readonly login_security_key: null;\n  readonly mfa_method_chooser: null;\n  readonly mfa_otp_secret_creation: MFAOTPSecretCreationPayload;\n  readonly mfa_security_key_creation: null;\n  readonly onboarding_create_passkey: null;\n  readonly onboarding_email: null;\n  readonly onboarding_username: null;\n  readonly onboarding_verify_passkey_attestation: OnboardingVerifyPasskeyAttestationPayload;\n  readonly passcode_confirmation: PasscodeConfirmationPayload;\n  readonly password_creation: null;\n  readonly preflight: null;\n  readonly profile_init: ProfilePayload;\n  readonly registration_init: null;\n  readonly success: SuccessPayload;\n  readonly thirdparty: ThirdPartyPayload;\n  readonly webauthn_credential_verification: OnboardingVerifyPasskeyAttestationPayload;\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/Hanko.spec.ts",
    "content": "import { HttpClient, Relay, Hanko } from \"../src\";\n\ndescribe(\"class hanko\", () => {\n  it(\"should hold instances of available Hanko API clients\", async () => {\n    const hanko = new Hanko(\"http://api.test\");\n\n    expect(hanko.client).toBeInstanceOf(HttpClient);\n    expect(hanko.relay).toBeInstanceOf(Relay);\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/Cookie.spec.ts",
    "content": "import JSCookie from \"js-cookie\";\nimport { Cookie } from \"../../src/lib/Cookie\";\nimport { fakeTimerNow } from \"../setup\";\n\ndescribe(\"Cookie()\", () => {\n  let cookie: Cookie;\n\n  beforeEach(() => {\n    cookie = new Cookie({ cookieName: \"hanko\" });\n  });\n\n  describe(\"cookie.setAuthCookie()\", () => {\n    it(\"should set a new cookie\", async () => {\n      jest.spyOn(JSCookie, \"set\");\n      cookie.setAuthCookie(\"test-token\", { secure: false });\n      expect(JSCookie.set).toHaveBeenCalledWith(\"hanko\", \"test-token\", {\n        secure: false,\n        sameSite: \"lax\",\n        domain: undefined,\n      });\n    });\n\n    it(\"should set a new secure cookie\", async () => {\n      jest.spyOn(JSCookie, \"set\");\n      cookie.setAuthCookie(\"test-token\");\n\n      expect(JSCookie.set).toHaveBeenCalledWith(\"hanko\", \"test-token\", {\n        secure: true,\n        domain: undefined,\n        sameSite: \"lax\",\n      });\n    });\n\n    it(\"should set a new cookie with expiration\", async () => {\n      jest.spyOn(JSCookie, \"set\");\n      const expires = new Date(fakeTimerNow + 60);\n      cookie.setAuthCookie(\"test-token\", { secure: true, expires });\n      expect(JSCookie.set).toHaveBeenCalledWith(\"hanko\", \"test-token\", {\n        secure: true,\n        sameSite: \"lax\",\n        domain: undefined,\n        expires,\n      });\n    });\n\n    it(\"should set a new cookie with given SameSite value\", async () => {\n      jest.spyOn(JSCookie, \"set\");\n      cookie.setAuthCookie(\"test-token\", { sameSite: \"strict\" });\n      expect(JSCookie.set).toHaveBeenCalledWith(\"hanko\", \"test-token\", {\n        secure: true,\n        sameSite: \"strict\",\n      });\n    });\n\n    it(\"should throw if not Secure and SameSite value is none\", async () => {\n      jest.spyOn(JSCookie, \"set\");\n      expect(() => {\n        cookie.setAuthCookie(\"test-token\", { secure: false, sameSite: \"none\" });\n      }).toThrow(\"Technical error\");\n    });\n\n    it(\"should set a new cookie with given domain value\", async () => {\n      jest.spyOn(JSCookie, \"set\");\n      cookie.setAuthCookie(\"test-token\", { domain: \".test.app\" });\n      expect(JSCookie.set).toHaveBeenCalledWith(\"hanko\", \"test-token\", {\n        secure: true,\n        sameSite: \"lax\",\n        domain: \".test.app\",\n      });\n    });\n  });\n\n  describe(\"cookie.getAuthCookie()\", () => {\n    it(\"should return the contents of the authorization cookie\", async () => {\n      JSCookie.get = jest.fn().mockReturnValue(\"test-token\");\n      const token = cookie.getAuthCookie();\n\n      expect(JSCookie.get).toHaveBeenCalledWith(\"hanko\");\n      expect(token).toBe(\"test-token\");\n    });\n  });\n\n  describe(\"cookie.removeAuthCookie()\", () => {\n    it(\"should return the contents of the authorization cookie\", async () => {\n      jest.spyOn(JSCookie, \"remove\");\n      cookie.removeAuthCookie();\n\n      expect(JSCookie.remove).toHaveBeenCalledWith(\"hanko\");\n    });\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/Throttle.spec.ts",
    "content": "import { Throttle } from \"../../src/lib/Throttle\";\n\ndescribe(\"throttle()\", () => {\n  let mockFn: jest.Mock;\n\n  beforeEach(() => {\n    jest.useFakeTimers();\n    mockFn = jest.fn();\n  });\n\n  afterEach(() => {\n    jest.clearAllMocks();\n    jest.clearAllTimers();\n    jest.useRealTimers();\n  });\n\n  it(\"should throttle the function to once per specified interval\", async () => {\n    const throttledFn = Throttle.throttle(mockFn, 1000);\n\n    expect(mockFn).not.toHaveBeenCalled();\n\n    throttledFn();\n    expect(mockFn).toHaveBeenCalledTimes(1);\n\n    throttledFn();\n    expect(mockFn).toHaveBeenCalledTimes(1);\n\n    jest.advanceTimersByTime(1000);\n    expect(mockFn).toHaveBeenCalledTimes(2);\n  });\n\n  it(\"should allow leading edge calls to be disabled\", async () => {\n    const throttledFunction = Throttle.throttle(mockFn, 1000, {\n      leading: false,\n    });\n\n    expect(mockFn).not.toHaveBeenCalled();\n\n    throttledFunction();\n    expect(mockFn).not.toHaveBeenCalled();\n\n    jest.advanceTimersByTime(500);\n    expect(mockFn).not.toHaveBeenCalled();\n\n    jest.advanceTimersByTime(500);\n    expect(mockFn).toHaveBeenCalledTimes(1);\n\n    throttledFunction();\n    jest.advanceTimersByTime(1000);\n    expect(mockFn).toHaveBeenCalledTimes(2);\n  });\n\n  it(\"should allow trailing edge calls to be disabled\", async () => {\n    const throttledFunction = Throttle.throttle(mockFn, 1000, {\n      trailing: false,\n    });\n\n    expect(mockFn).not.toHaveBeenCalled();\n\n    throttledFunction();\n    expect(mockFn).toHaveBeenCalledTimes(1);\n\n    throttledFunction();\n    throttledFunction();\n    throttledFunction();\n\n    jest.advanceTimersByTime(500);\n    expect(mockFn).toHaveBeenCalledTimes(1);\n\n    jest.advanceTimersByTime(500);\n    expect(mockFn).toHaveBeenCalledTimes(1);\n\n    throttledFunction();\n    jest.advanceTimersByTime(1000);\n    expect(mockFn).toHaveBeenCalledTimes(2);\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/WebauthnSupport.spec.ts",
    "content": "import { WebauthnSupport } from \"../../src\";\nimport { fakePublicKeyCredential } from \"../setup\";\n\ndescribe(\"WebauthnSupport.supported()\", () => {\n  it(\"should support webauthn\", async () => {\n    expect(WebauthnSupport.supported()).toEqual(true);\n  });\n});\n\ndescribe(\"WebauthnSupport.isPlatformAuthenticatorAvailable()\", () => {\n  it(\"should pass if platform authenticators are supported\", async () => {\n    jest\n      .spyOn(\n        PublicKeyCredential,\n        \"isUserVerifyingPlatformAuthenticatorAvailable\"\n      )\n      .mockResolvedValueOnce(true);\n\n    const supported = await WebauthnSupport.isPlatformAuthenticatorAvailable();\n\n    expect(supported).toBe(true);\n  });\n\n  it(\"should fail if platform authenticators are unavailable\", async () => {\n    jest\n      .spyOn(\n        PublicKeyCredential,\n        \"isUserVerifyingPlatformAuthenticatorAvailable\"\n      )\n      .mockResolvedValueOnce(false);\n\n    const supported = await WebauthnSupport.isPlatformAuthenticatorAvailable();\n\n    expect(supported).toBe(false);\n  });\n\n  it(\"should fail if webauthn is not supported\", async () => {\n    jest.spyOn(WebauthnSupport, \"supported\").mockReturnValueOnce(false);\n    const supported = await WebauthnSupport.isPlatformAuthenticatorAvailable();\n\n    expect(supported).toBe(false);\n  });\n});\n\ndescribe(\"WebauthnSupport.isSecurityKeySupported()\", () => {\n  beforeEach(() => {\n    Object.defineProperty(window, \"PublicKeyCredential\", {\n      value: fakePublicKeyCredential,\n      configurable: true,\n      writable: true,\n    });\n  });\n\n  it(\"should pass if security keys are supported\", async () => {\n    jest\n      .spyOn(window.PublicKeyCredential, \"isExternalCTAP2SecurityKeySupported\")\n      .mockResolvedValueOnce(true);\n\n    const supported = await WebauthnSupport.isSecurityKeySupported();\n\n    expect(supported).toBe(true);\n  });\n\n  it(\"should pass if security keys are not supported\", async () => {\n    jest\n      .spyOn(window.PublicKeyCredential, \"isExternalCTAP2SecurityKeySupported\")\n      .mockResolvedValueOnce(false);\n\n    const supported = await WebauthnSupport.isSecurityKeySupported();\n\n    expect(supported).toBe(false);\n  });\n\n  it(\"should fail if webauthn is not supported\", async () => {\n    window.PublicKeyCredential = undefined;\n    jest.spyOn(WebauthnSupport, \"supported\").mockImplementation(() => false);\n\n    const supported = await WebauthnSupport.isSecurityKeySupported();\n\n    expect(supported).toEqual(false);\n    expect(WebauthnSupport.supported).toHaveBeenCalled();\n  });\n});\n\ndescribe(\"WebauthnSupport.isConditionalMediationAvailable()\", () => {\n  beforeEach(() => {\n    Object.defineProperty(window, \"PublicKeyCredential\", {\n      value: fakePublicKeyCredential,\n      configurable: true,\n      writable: true,\n    });\n  });\n\n  it(\"should pass if autofilled requests are supported\", async () => {\n    jest\n      .spyOn(window.PublicKeyCredential, \"isConditionalMediationAvailable\")\n      .mockResolvedValueOnce(true);\n\n    const supported = await WebauthnSupport.isConditionalMediationAvailable();\n\n    expect(supported).toBe(true);\n  });\n\n  it(\"should pass if autofilled requests  are not supported\", async () => {\n    jest\n      .spyOn(window.PublicKeyCredential, \"isConditionalMediationAvailable\")\n      .mockResolvedValueOnce(false);\n\n    const supported = await WebauthnSupport.isConditionalMediationAvailable();\n\n    expect(supported).toBe(false);\n  });\n\n  it(\"should fail if webauthn is not supported\", async () => {\n    window.PublicKeyCredential = undefined;\n    const supported = await WebauthnSupport.isConditionalMediationAvailable();\n\n    expect(supported).toEqual(false);\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/client/HttpClient.spec.ts",
    "content": "import {\n  Headers,\n  Response,\n  HttpClient,\n} from \"../../../src/lib/client/HttpClient\";\nimport { RequestTimeoutError, TechnicalError } from \"../../../src\";\nimport { fakeTimerNow } from \"../../setup\";\n\nconst jwt = \"test-token\";\nlet httpClient: HttpClient;\nlet xhr: XMLHttpRequest;\n\nbeforeEach(() => {\n  Object.defineProperty(global, \"XMLHttpRequest\", {\n    value: jest.fn().mockImplementation(() => ({\n      response: JSON.stringify({ foo: \"bar\" }),\n      open: jest.fn(),\n      setRequestHeader: jest.fn(),\n      getResponseHeader: jest.fn(),\n      getAllResponseHeaders: jest.fn().mockReturnValue(`X-Auth-Token: ${jwt}`),\n      send: jest.fn(),\n    })),\n    configurable: true,\n    writable: true,\n  });\n\n  httpClient = new HttpClient(\"http://test.api\", {\n    cookieName: \"hanko\",\n    localStorageKey: \"hanko\",\n    timeout: 13000,\n    sessionTokenLocation: \"cookie\",\n  });\n  xhr = new XMLHttpRequest();\n});\n\ndescribe(\"httpClient._fetch()\", () => {\n  it(\"should perform http requests\", async () => {\n    jest.spyOn(xhr, \"send\").mockImplementation(function () {\n      // eslint-disable-next-line no-invalid-this\n      this.onload();\n    });\n\n    const response = await httpClient._fetch(\"/test\", { method: \"GET\" }, xhr);\n\n    expect(xhr.setRequestHeader).toHaveBeenCalledWith(\n      \"Accept\",\n      \"application/json\",\n    );\n    expect(xhr.setRequestHeader).toHaveBeenCalledWith(\n      \"Content-Type\",\n      \"application/json\",\n    );\n    expect(xhr.setRequestHeader).toHaveBeenCalledTimes(3);\n    expect(xhr.open).toHaveBeenNthCalledWith(\n      1,\n      \"GET\",\n      \"http://test.api/test\",\n      true,\n    );\n    expect(response.json()).toEqual({ foo: \"bar\" });\n  });\n\n  it(\"should set authorization request headers when cookie is available\", async () => {\n    jest.spyOn(xhr, \"send\").mockImplementation(function () {\n      // eslint-disable-next-line no-invalid-this\n      this.onload();\n    });\n\n    jest.spyOn(httpClient.cookie, \"getAuthCookie\").mockReturnValue(jwt);\n\n    await httpClient._fetch(\"/test\", { method: \"GET\" }, xhr);\n\n    expect(xhr.setRequestHeader).toHaveBeenCalledWith(\n      \"Authorization\",\n      `Bearer ${jwt}`,\n    );\n    expect(xhr.setRequestHeader).toHaveBeenCalledTimes(4);\n  });\n\n  it(\"should handle onerror\", async () => {\n    jest.spyOn(xhr, \"send\").mockImplementation(function () {\n      // eslint-disable-next-line no-invalid-this\n      this.onerror();\n    });\n\n    const response = httpClient._fetch(\"/test\", { method: \"GET\" }, xhr);\n\n    await expect(response).rejects.toThrow(TechnicalError);\n  });\n\n  it(\"should handle ontimeout\", async () => {\n    jest.spyOn(xhr, \"send\").mockImplementation(function () {\n      // eslint-disable-next-line no-invalid-this\n      this.ontimeout();\n    });\n\n    const response = httpClient._fetch(\"/test\", { method: \"GET\" }, xhr);\n\n    await expect(response).rejects.toThrow(RequestTimeoutError);\n  });\n});\n\ndescribe(\"httpClient.get()\", () => {\n  it(\"should call get with correct args\", async () => {\n    httpClient._fetch = jest.fn();\n    await httpClient.get(\"/test\");\n\n    expect(httpClient._fetch).toHaveBeenCalledWith(\"/test\", { method: \"GET\" });\n  });\n});\n\ndescribe(\"httpClient.post()\", () => {\n  it(\"should call post with correct args\", async () => {\n    httpClient._fetch = jest.fn();\n    await httpClient.post(\"/test\");\n\n    expect(httpClient._fetch).toHaveBeenCalledWith(\"/test\", { method: \"POST\" });\n  });\n});\n\ndescribe(\"httpClient.put()\", () => {\n  it(\"should call put with correct args\", async () => {\n    httpClient._fetch = jest.fn();\n    await httpClient.put(\"/test\");\n\n    expect(httpClient._fetch).toHaveBeenCalledWith(\"/test\", { method: \"PUT\" });\n  });\n});\n\ndescribe(\"httpClient.patch()\", () => {\n  it(\"should call patch with correct args\", async () => {\n    httpClient._fetch = jest.fn();\n    await httpClient.patch(\"/test\");\n\n    expect(httpClient._fetch).toHaveBeenCalledWith(\"/test\", {\n      method: \"PATCH\",\n    });\n  });\n});\n\ndescribe(\"httpClient.delete()\", () => {\n  it(\"should call delete with correct args\", async () => {\n    httpClient._fetch = jest.fn();\n    await httpClient.delete(\"/test\");\n\n    expect(httpClient._fetch).toHaveBeenCalledWith(\"/test\", {\n      method: \"DELETE\",\n    });\n  });\n});\n\ndescribe(\"headers.get()\", () => {\n  it(\"should return headers\", async () => {\n    const header = new Headers(xhr);\n\n    jest.spyOn(xhr, \"getResponseHeader\").mockReturnValue(\"bar\");\n\n    expect(header.getResponseHeader(\"foo\")).toEqual(\"bar\");\n  });\n});\n\ndescribe(\"httpClient.processResponseHeadersOnLogin()\", () => {\n  describe(\"when the x-auth-token is available in the response header\", () => {\n    const jwt = \"test-jwt\";\n    const expirationSeconds = 7;\n    const realLocation = window.location;\n\n    beforeEach(() => {\n      Object.defineProperty(global, \"XMLHttpRequest\", {\n        value: jest.fn().mockImplementation(() => ({\n          response: JSON.stringify({ foo: \"bar\" }),\n          open: jest.fn(),\n          setRequestHeader: jest.fn(),\n          getResponseHeader: jest\n            .fn()\n            .mockImplementation((name: string) =>\n              name === \"X-Auth-Token\"\n                ? jwt\n                : name === \"X-Session-Lifetime\"\n                ? `${expirationSeconds}`\n                : \"\",\n            ),\n          getAllResponseHeaders: jest\n            .fn()\n            .mockReturnValue(\"X-Auth-Token: ...\\r\\nX-Session-Lifetime: ...\"),\n          send: jest.fn(),\n        })),\n        configurable: true,\n        writable: true,\n      });\n\n      delete window.location;\n    });\n\n    afterEach(() => {\n      window.location = realLocation;\n    });\n\n    it.each`\n      protocolApi | protocolClient | secure\n      ${\"http\"}   | ${\"http\"}      | ${false}\n      ${\"http\"}   | ${\"https\"}     | ${false}\n      ${\"https\"}  | ${\"http\"}      | ${false}\n      ${\"https\"}  | ${\"https\"}     | ${true}\n    `(\n      \"when the API uses $protocolApi and the client uses $protocolClient, should set client state and cookie with secure flag set to $secure\",\n      async ({ protocolApi, protocolClient, secure }) => {\n        const client = new HttpClient(`${protocolApi}://test.api`, {\n          cookieName: \"hanko\",\n          localStorageKey: \"hanko\",\n          timeout: 13000,\n          sessionTokenLocation: \"cookie\",\n        });\n        const xhr = new XMLHttpRequest();\n        const response = new Response(xhr);\n\n        // @ts-ignore\n        window.location = {\n          href: `${protocolClient}://test.app`,\n        };\n\n        jest.spyOn(response.xhr, \"getResponseHeader\");\n        jest.spyOn(client.cookie, \"setAuthCookie\");\n\n        client.processHeaders(xhr);\n\n        expect(response.xhr.getResponseHeader).toHaveBeenCalledTimes(2);\n\n        expect(client.cookie.setAuthCookie).toHaveBeenCalledTimes(1);\n\n        expect(client.cookie.setAuthCookie).toHaveBeenCalledWith(jwt, {\n          secure,\n          expires: new Date(fakeTimerNow + expirationSeconds * 1000),\n        });\n        expect(client.cookie.setAuthCookie).toHaveBeenCalledTimes(1);\n      },\n    );\n  });\n});\n\ndescribe(\"response.parseRetryAfterHeader()\", () => {\n  it.each`\n    headerValue  | expected\n    ${\"\"}        | ${0}\n    ${\"0\"}       | ${0}\n    ${\"3\"}       | ${3}\n    ${\"-3\"}      | ${-3}\n    ${\"invalid\"} | ${0}\n  `(\"should parse retry-after header\", async ({ headerValue, expected }) => {\n    const response = new Response(xhr);\n    jest.spyOn(xhr, \"getResponseHeader\").mockReturnValue(headerValue);\n    const result = response.parseNumericHeader(\"Retry-After\");\n    expect(xhr.getResponseHeader).toHaveBeenCalledWith(\"Retry-After\");\n    expect(result).toBe(expected);\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/client/UserClient.spec.ts",
    "content": "/* eslint-env jest */\nimport { UserClient } from \"../../../src\";\nimport { Response } from \"../../../src/lib/client/HttpClient\";\n\nlet userClient: UserClient;\n\nbeforeEach(() => {\n  userClient = new UserClient(\"http://test.api\", {\n    cookieName: \"hanko\",\n    timeout: 13000,\n    sessionTokenLocation: \"cookie\",\n  });\n});\n\ndescribe(\"UserClient.getCurrent()\", () => {\n  const userID = \"test-user-1\";\n  const email = \"test-email-1@test\";\n  const credentials = [{ id: \"test-credential-1\" }];\n\n  it(\"should retrieve currently logged in user\", async () => {\n    const responseMe = new Response(new XMLHttpRequest());\n    responseMe.ok = true;\n    responseMe._decodedJSON = {\n      id: userID,\n    };\n\n    const responseUser = new Response(new XMLHttpRequest());\n    responseUser.ok = true;\n    responseUser._decodedJSON = {\n      id: userID,\n      email,\n      webauthn_credentials: credentials,\n    };\n\n    jest\n      .spyOn(userClient.client, \"get\")\n      .mockResolvedValueOnce(responseMe)\n      .mockResolvedValueOnce(responseUser);\n\n    const user = userClient.getCurrent();\n    await expect(user).resolves.toBe(responseUser._decodedJSON);\n\n    expect(userClient.client.get).toHaveBeenNthCalledWith(1, \"/me\");\n    expect(userClient.client.get).toHaveBeenNthCalledWith(\n      2,\n      `/users/${userID}`,\n    );\n  });\n\n  it.each`\n    statusMe | statusUsers | error\n    ${400}   | ${200}      | ${\"Technical error\"}\n    ${401}   | ${200}      | ${\"Unauthorized error\"}\n    ${404}   | ${200}      | ${\"Technical error\"}\n    ${200}   | ${400}      | ${\"Technical error\"}\n    ${200}   | ${401}      | ${\"Unauthorized error\"}\n    ${200}   | ${404}      | ${\"Technical error\"}\n    ${200}   | ${500}      | ${\"Technical error\"}\n    ${500}   | ${200}      | ${\"Technical error\"}\n  `(\n    \"should throw error if API returns an error status\",\n    async ({ statusMe, statusUsers, error }) => {\n      const responseMe = new Response(new XMLHttpRequest());\n      responseMe.status = statusMe;\n      responseMe.ok = statusMe >= 200 && statusMe <= 299;\n\n      const responseUser = new Response(new XMLHttpRequest());\n      responseUser.status = statusUsers;\n      responseUser.ok = statusUsers >= 200 && statusUsers <= 299;\n\n      jest\n        .spyOn(userClient.client, \"get\")\n        .mockResolvedValueOnce(responseMe)\n        .mockResolvedValueOnce(responseUser);\n\n      const user = userClient.getCurrent();\n      await expect(user).rejects.toThrow(error);\n    },\n  );\n\n  it(\"should throw error on API communication failure\", async () => {\n    userClient.client.get = jest\n      .fn()\n      .mockRejectedValue(new Error(\"Test error\"));\n\n    const user = userClient.getCurrent();\n    await expect(user).rejects.toThrow(\"Test error\");\n  });\n});\n\ndescribe(\"UserClient.getCurrentUser()\", () => {\n  const userID = \"test-user-1\";\n\n  it(\"should retrieve currently logged in user\", async () => {\n    const responseMe = new Response(new XMLHttpRequest());\n    responseMe.ok = true;\n\n    responseMe._decodedJSON = {\n      user_id: userID,\n      emails: [\n        {\n          id: \"test-email-1\",\n          address: \"test-email-1@test\",\n          is_verified: true,\n          is_primary: true,\n        },\n      ],\n      created_at: \"2024-01-01T00:00:00Z\",\n      updated_at: \"2024-01-01T00:00:00Z\",\n    };\n\n    jest.spyOn(userClient.client, \"get\").mockResolvedValueOnce(responseMe);\n\n    const user = userClient.getCurrentUser();\n    await expect(user).resolves.toBe(responseMe._decodedJSON);\n\n    expect(userClient.client.get).toHaveBeenCalledTimes(1);\n    expect(userClient.client.get).toHaveBeenCalledWith(\"/me\");\n  });\n\n  it.each`\n    status | error\n    ${400} | ${\"Technical error\"}\n    ${401} | ${\"Unauthorized error\"}\n    ${404} | ${\"Technical error\"}\n    ${500} | ${\"Technical error\"}\n  `(\n    \"should throw error if API returns an error status\",\n    async ({ status, error }) => {\n      const responseMe = new Response(new XMLHttpRequest());\n      responseMe.status = status;\n      responseMe.ok = status >= 200 && status <= 299;\n\n      jest.spyOn(userClient.client, \"get\").mockResolvedValueOnce(responseMe);\n\n      const user = userClient.getCurrentUser();\n      await expect(user).rejects.toThrow(error);\n    },\n  );\n\n  it(\"should throw error on API communication failure\", async () => {\n    userClient.client.get = jest\n      .fn()\n      .mockRejectedValue(new Error(\"Test error\"));\n\n    const user = userClient.getCurrentUser();\n    await expect(user).rejects.toThrow(\"Test error\");\n  });\n});\n\ndescribe(\"UserClient.logout()\", () => {\n  it.each`\n    status\n    ${200}\n    ${401}\n  `(\"should return true if logout is successful\", async ({ status }) => {\n    const response = new Response(new XMLHttpRequest());\n    response.status = status;\n    response.ok = status >= 200 && status <= 299;\n\n    jest.spyOn(userClient.client, \"post\").mockResolvedValueOnce(response);\n    await expect(userClient.logout()).resolves.not.toThrow();\n\n    expect(userClient.client.post).toHaveBeenCalledWith(\"/logout\");\n  });\n\n  it.each`\n    status | error\n    ${400} | ${\"Technical error\"}\n    ${404} | ${\"Technical error\"}\n    ${500} | ${\"Technical error\"}\n  `(\n    \"should throw error if API returns an error status\",\n    async ({ status, error }) => {\n      const response = new Response(new XMLHttpRequest());\n      response.status = status;\n      response.ok = status >= 200 && status <= 299;\n\n      jest.spyOn(userClient.client, \"post\").mockResolvedValueOnce(response);\n\n      await expect(userClient.logout()).rejects.toThrow(error);\n\n      expect(userClient.client.post).toHaveBeenCalledWith(\"/logout\");\n    },\n  );\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/events/Dispatcher.spec.ts",
    "content": "import { Dispatcher } from \"../../../src/lib/events/Dispatcher\";\nimport { CustomEventWithDetail, SessionDetail } from \"../../../src\";\n\ndescribe(\"Dispatcher\", () => {\n  let dispatcher: Dispatcher;\n\n  beforeEach(() => {\n    dispatcher = new Dispatcher();\n  });\n\n  describe(\"dispatchSessionCreatedEvent()\", () => {\n    it(\"dispatches a custom event with the 'hanko-session-created' type and the provided detail\", () => {\n      const detail = {\n        claims: {\n          subject: \"test\",\n          expiration: \"test\",\n          session_id: \"test\",\n        },\n        expirationSeconds: 7,\n      };\n      const dispatchEventSpy = jest.spyOn(dispatcher, \"_dispatchEvent\");\n\n      dispatcher.dispatchSessionCreatedEvent(detail);\n\n      expect(dispatchEventSpy).toHaveBeenCalledTimes(1);\n      expect(dispatchEventSpy).toHaveBeenCalledWith(\n        new CustomEventWithDetail(\"hanko-session-created\", detail),\n      );\n      const event = dispatchEventSpy.mock\n        .calls[0][0] as CustomEventWithDetail<SessionDetail>;\n      expect(event.type).toEqual(\"hanko-session-created\");\n      expect(event.detail).toBe(detail);\n    });\n  });\n\n  describe(\"dispatchSessionExpiredEvent()\", () => {\n    it(\"dispatches a custom event with the 'hanko-session-expired' type and null detail\", () => {\n      const dispatchEventSpy = jest.spyOn(dispatcher, \"_dispatchEvent\");\n\n      dispatcher.dispatchSessionExpiredEvent();\n\n      expect(dispatchEventSpy).toHaveBeenCalledTimes(1);\n      expect(dispatchEventSpy).toHaveBeenCalledWith(\n        new CustomEventWithDetail(\"hanko-session-expired\", null),\n      );\n      const event = dispatchEventSpy.mock\n        .calls[0][0] as CustomEventWithDetail<null>;\n      expect(event.type).toEqual(\"hanko-session-expired\");\n    });\n  });\n\n  describe(\"dispatchUserDeletedEvent()\", () => {\n    it(\"dispatches a custom event with the 'hanko-user-deleted' type and null detail\", () => {\n      const dispatchEventSpy = jest.spyOn(dispatcher, \"_dispatchEvent\");\n\n      dispatcher.dispatchUserDeletedEvent();\n\n      expect(dispatchEventSpy).toHaveBeenCalledTimes(1);\n      expect(dispatchEventSpy).toHaveBeenCalledWith(\n        new CustomEventWithDetail(\"hanko-user-deleted\", null),\n      );\n      const event = dispatchEventSpy.mock\n        .calls[0][0] as CustomEventWithDetail<null>;\n      expect(event.type).toEqual(\"hanko-user-deleted\");\n    });\n  });\n\n  describe(\"dispatchUserLoggedOutEvent()\", () => {\n    it(\"dispatches a custom event with the 'hanko-user-logged-out' type and null detail\", () => {\n      const dispatchEventSpy = jest.spyOn(dispatcher, \"_dispatchEvent\");\n\n      dispatcher.dispatchUserLoggedOutEvent();\n\n      expect(dispatchEventSpy).toHaveBeenCalledTimes(1);\n      expect(dispatchEventSpy).toHaveBeenCalledWith(\n        new CustomEventWithDetail(\"hanko-user-logged-out\", null),\n      );\n      const event = dispatchEventSpy.mock\n        .calls[0][0] as CustomEventWithDetail<null>;\n      expect(event.type).toEqual(\"hanko-user-logged-out\");\n    });\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/events/Listener.spec.ts",
    "content": "import { Listener } from \"../../../src/lib/events/Listener\";\nimport {\n  sessionCreatedType,\n  sessionExpiredType,\n  userDeletedType,\n  userLoggedOutType,\n} from \"../../../src\";\n\ndescribe(\"Listener()\", () => {\n  let listener: Listener;\n  let addEventListenerSpy: jest.SpyInstance;\n  let mockThrottleFunc: jest.SpyInstance;\n  let mockCallback: jest.Mock;\n\n  beforeEach(() => {\n    listener = new Listener();\n    addEventListenerSpy = jest.spyOn(listener, \"_addEventListener\");\n    mockThrottleFunc = jest.spyOn(listener, \"_throttle\");\n    mockCallback = jest.fn();\n    jest.useFakeTimers({ now: new Date() });\n  });\n\n  afterEach(() => {\n    jest.restoreAllMocks();\n    jest.clearAllTimers();\n  });\n\n  describe(\"onSessionCreated()\", () => {\n    it(\"should add an event listener for session created events\", async () => {\n      const mockDetail = {\n        userID: \"testUser\",\n        jwt: \"testJWT\",\n        expirationSeconds: 7,\n      };\n\n      listener.onSessionCreated(mockCallback);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        sessionCreatedType,\n        expect.any(Function),\n        { once: false },\n      );\n\n      expect(mockThrottleFunc).toHaveBeenCalledWith(\n        expect.any(Function),\n        listener.throttleLimit,\n        {\n          leading: true,\n          trailing: false,\n        },\n      );\n\n      const mockEvent = new CustomEvent(sessionCreatedType, {\n        detail: mockDetail,\n      });\n\n      // should throttle\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledWith(mockDetail);\n      expect(mockCallback).toHaveBeenCalledTimes(1);\n    });\n\n    it(\"should only execute the callback once\", async () => {\n      const mockDetail = {\n        userID: \"testUser\",\n        jwt: \"testJWT\",\n        expirationSeconds: 7,\n      };\n\n      const mockEvent = new CustomEvent(sessionCreatedType, {\n        detail: mockDetail,\n      });\n\n      listener.onSessionCreated(mockCallback, true);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        sessionCreatedType,\n        expect.any(Function),\n        { once: true },\n      );\n\n      document.dispatchEvent(mockEvent);\n      jest.advanceTimersByTime(1100); // skip throttle\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledTimes(1);\n    });\n\n    it(\"should clean up the event listener\", async () => {\n      const mockDetail = {\n        userID: \"testUser\",\n        jwt: \"testJWT\",\n        expirationSeconds: 7,\n      };\n\n      const mockEvent = new CustomEvent(sessionCreatedType, {\n        detail: mockDetail,\n      });\n\n      const cleanup = listener.onSessionCreated(mockCallback, true);\n\n      cleanup();\n\n      document.dispatchEvent(mockEvent);\n      expect(mockCallback).toHaveBeenCalledTimes(0);\n    });\n  });\n\n  describe(\"onSessionExpired()\", () => {\n    it(\"should add an event listener for session expired events\", async () => {\n      listener.onSessionExpired(mockCallback);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        sessionExpiredType,\n        expect.any(Function),\n        { once: false },\n      );\n\n      expect(mockThrottleFunc).toHaveBeenCalledWith(\n        expect.any(Function),\n        listener.throttleLimit,\n        {\n          leading: true,\n          trailing: false,\n        },\n      );\n\n      const mockEvent = new CustomEvent(sessionExpiredType, {});\n\n      // should throttle\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledTimes(1);\n    });\n\n    it(\"should only execute the callback once\", async () => {\n      const mockEvent = new CustomEvent(sessionExpiredType, {});\n\n      listener.onSessionExpired(mockCallback, true);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        sessionExpiredType,\n        expect.any(Function),\n        { once: true },\n      );\n\n      document.dispatchEvent(mockEvent);\n      jest.advanceTimersByTime(1000); // skip throttle\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledTimes(1);\n    });\n\n    it(\"should clean up the event listener\", async () => {\n      const mockEvent = new CustomEvent(sessionExpiredType, {});\n\n      const cleanup = listener.onSessionExpired(mockCallback, true);\n\n      cleanup();\n\n      document.dispatchEvent(mockEvent);\n      expect(mockCallback).toHaveBeenCalledTimes(0);\n    });\n  });\n\n  describe(\"onUserLogged()\", () => {\n    it(\"should add an event listener for user logged out events\", async () => {\n      listener.onUserLoggedOut(mockCallback);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        userLoggedOutType,\n        expect.any(Function),\n        { once: false },\n      );\n\n      expect(mockThrottleFunc).toHaveBeenCalledTimes(0);\n\n      const mockEvent = new CustomEvent(userLoggedOutType, {});\n\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledTimes(3);\n    });\n\n    it(\"should only execute the callback once\", async () => {\n      const mockEvent = new CustomEvent(userLoggedOutType, {});\n\n      listener.onUserLoggedOut(mockCallback, true);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        userLoggedOutType,\n        expect.any(Function),\n        { once: true },\n      );\n\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledTimes(1);\n    });\n\n    it(\"should clean up the event listener\", async () => {\n      const mockEvent = new CustomEvent(userLoggedOutType, {});\n\n      const cleanup = listener.onUserLoggedOut(mockCallback, true);\n\n      cleanup();\n\n      document.dispatchEvent(mockEvent);\n      expect(mockCallback).toHaveBeenCalledTimes(0);\n    });\n  });\n\n  describe(\"onUserDeleted()\", () => {\n    it(\"should add an event listener for user deleted events\", async () => {\n      listener.onUserDeleted(mockCallback);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        userDeletedType,\n        expect.any(Function),\n        { once: false },\n      );\n\n      expect(mockThrottleFunc).toHaveBeenCalledTimes(0);\n\n      const mockEvent = new CustomEvent(userDeletedType, {});\n\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledTimes(3);\n    });\n\n    it(\"should only execute the callback once\", async () => {\n      const mockEvent = new CustomEvent(userDeletedType, {});\n\n      listener.onUserDeleted(mockCallback, true);\n\n      expect(addEventListenerSpy).toHaveBeenCalledWith(\n        userDeletedType,\n        expect.any(Function),\n        { once: true },\n      );\n\n      document.dispatchEvent(mockEvent);\n      document.dispatchEvent(mockEvent);\n\n      expect(mockCallback).toHaveBeenCalledTimes(1);\n    });\n\n    it(\"should clean up the event listener\", async () => {\n      const mockEvent = new CustomEvent(userDeletedType, {});\n\n      const cleanup = listener.onUserDeleted(mockCallback, true);\n\n      cleanup();\n\n      document.dispatchEvent(mockEvent);\n      expect(mockCallback).toHaveBeenCalledTimes(0);\n    });\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/lib/flow-api/State.spec.ts",
    "content": "import {\n  State,\n  Action,\n  Hanko,\n  FlowResponse,\n  AnyState,\n  FlowName,\n} from \"../../../src\";\nimport { autoSteps } from \"../../../src/lib/flow-api/auto-steps\";\n\njest.mock(\"../../../src\", () => {\n  const actual = jest.requireActual(\"../../../src\");\n  return {\n    ...actual,\n    Hanko: jest.fn(() => ({\n      client: {\n        post: jest.fn<() => Promise<Response>, [string, any?]>(),\n      },\n      relay: {\n        dispatchAfterStateChangeEvent: jest.fn(),\n        dispatchBeforeStateChangeEvent: jest.fn(),\n      },\n    })),\n  };\n});\n\njest.mock(\"../../../src/lib/flow-api/auto-steps\", () => ({\n  autoSteps: {\n    preflight: jest.fn(),\n  },\n}));\n\njest.mock(\"../../../src/lib/flow-api/passkey-autofill-activation\", () => ({\n  passkeyAutofillActivationHandlers: {\n    somePasskeyState: jest.fn(),\n  },\n}));\n\ndescribe(\"State\", () => {\n  let hankoMock: jest.Mocked<Hanko>;\n  const flowName: FlowName = \"login\";\n  const defaultCacheKey = \"hanko-flow-state\";\n  const mockLoginInitResponse: FlowResponse<\"login_init\"> = {\n    name: \"login_init\",\n    csrf_token: \"csrf123\",\n    status: 200,\n    actions: {\n      continue_with_login_identifier: {\n        action: \"continue_with_login_identifier\",\n        href: \"/continue_with_login_identifier\",\n        inputs: {},\n        description: null,\n      },\n    },\n    payload: null,\n  };\n  const mockPreflightResponse: FlowResponse<\"preflight\"> = {\n    ...mockLoginInitResponse,\n    name: \"preflight\",\n    payload: null,\n    actions: {\n      register_client_capabilities: {\n        action: \"register_client_capabilities\",\n        href: \"/register_client_capabilities\",\n        inputs: {\n          webauthn_available: {\n            name: \"webauthn_available\",\n            type: \"boolean\",\n          },\n          webauthn_conditional_mediation_available: {\n            name: \"webauthn_conditional_mediation_available\",\n            type: \"boolean\",\n          },\n          webauthn_platform_authenticator_available: {\n            name: \"webauthn_platform_authenticator_available\",\n            type: \"boolean\",\n          },\n        },\n        description: \"\",\n      },\n    },\n  };\n\n  beforeEach(() => {\n    hankoMock = new (jest.requireMock(\n      \"../../../src\",\n    ).Hanko)() as jest.Mocked<Hanko>;\n    localStorage.clear();\n    jest.clearAllMocks();\n  });\n\n  describe(\"constructor\", () => {\n    it(\"initializes state properties correctly\", () => {\n      const state = new State(hankoMock, flowName, mockLoginInitResponse);\n      expect(state.name).toBe(\"login_init\");\n      expect(state.flowName).toBe(flowName);\n      expect(state.csrfToken).toBe(\"csrf123\");\n      expect(state.status).toBe(200);\n      expect(state.payload).toBeNull();\n      expect(state.hanko).toBe(hankoMock);\n      expect(state.actions.continue_with_login_identifier).toBeInstanceOf(\n        Action,\n      );\n      expect(state.isCached).toBe(false);\n      expect(state.cacheKey).toBe(\"hanko-flow-state\");\n    });\n\n    it(\"sets up autoStep if state is in autoSteps\", () => {\n      const state = new State(hankoMock, flowName, mockPreflightResponse);\n      expect(state.autoStep).toBeDefined();\n      expect(typeof state.autoStep).toBe(\"function\");\n    });\n\n    it(\"dispatches state change event by default\", () => {\n      new State(hankoMock, flowName, mockLoginInitResponse);\n      expect(\n        hankoMock.relay.dispatchAfterStateChangeEvent,\n      ).toHaveBeenCalledWith({\n        state: expect.any(State),\n      });\n    });\n  });\n\n  describe(\"buildActions\", () => {\n    it(\"creates actions map with proxy for undefined actions\", () => {\n      const state = new State(hankoMock, flowName, mockLoginInitResponse);\n      expect(state.actions.continue_with_login_identifier).toBeInstanceOf(\n        Action,\n      );\n      expect(state.actions.continue_with_login_identifier.enabled).toBe(true);\n      expect(state.actions.thirdparty_oauth).toBeInstanceOf(Action);\n      expect(state.actions.thirdparty_oauth.enabled).toBe(false);\n    });\n  });\n\n  describe(\"saveToLocalStorage\", () => {\n    it(\"saves serialized state to localStorage\", () => {\n      const state = new State(hankoMock, flowName, mockLoginInitResponse);\n      state.saveToLocalStorage();\n      expect(localStorage.setItem).toHaveBeenCalled();\n      const setItemCall = (localStorage.setItem as jest.Mock).mock.calls[0];\n      const [key, value] = setItemCall;\n      expect(key).toBe(defaultCacheKey);\n      const parsedValue = JSON.parse(value);\n      expect(parsedValue).toEqual({\n        ...mockLoginInitResponse,\n        flow_name: flowName,\n        is_cached: true,\n        previous_action: null,\n      });\n    });\n\n    it(\"uses custom cacheKey for cached data\", async () => {\n      const customCacheKey = \"custom-hanko-state\";\n      (localStorage.getItem as jest.Mock).mockReturnValue(\n        JSON.stringify({ ...mockLoginInitResponse, is_cached: true }),\n      );\n      const state = await State.create(hankoMock, flowName, {\n        cacheKey: customCacheKey,\n      });\n      expect(localStorage.getItem).toHaveBeenCalledWith(customCacheKey);\n      expect(state.cacheKey).toBe(customCacheKey);\n      expect(state.name).toBe(\"login_init\");\n    });\n  });\n\n  describe(\"static deserialize\", () => {\n    it(\"creates state from serialized data\", async () => {\n      const serializedState = {\n        ...mockLoginInitResponse,\n        flow_name: flowName,\n        is_cached: true,\n      };\n      const state = await State.deserialize(hankoMock, serializedState);\n      expect(state.name).toBe(\"login_init\");\n      expect(state.isCached).toBe(true);\n      expect(state.flowName).toBe(flowName);\n    });\n  });\n\n  describe(\"readFromLocalStorage\", () => {\n    it(\"returns undefined for invalid JSON\", () => {\n      (localStorage.getItem as jest.Mock).mockReturnValue(\"invalid-json\");\n      const result = State.readFromLocalStorage(defaultCacheKey);\n      expect(result).toBeUndefined();\n    });\n  });\n\n  describe(\"static create\", () => {\n    it(\"creates state from cached data if available\", async () => {\n      (localStorage.getItem as jest.Mock).mockReturnValue(\n        JSON.stringify({ ...mockLoginInitResponse, is_cached: true }),\n      );\n      const state = await State.create(hankoMock, flowName);\n      expect(hankoMock.client.post).not.toHaveBeenCalled();\n      expect(state.name).toBe(\"login_init\");\n      expect(state.isCached).toBe(true);\n    });\n\n    it(\"fetches state if no cached data\", async () => {\n      (localStorage.getItem as jest.Mock).mockReturnValue(null);\n      (hankoMock.client.post as jest.Mock).mockResolvedValue({\n        json: () => Promise.resolve(mockLoginInitResponse),\n      });\n      const state = await State.create(hankoMock, flowName);\n      expect(hankoMock.client.post).toHaveBeenCalled();\n      expect(state.name).toBe(\"login_init\");\n      expect(state.isCached).toBe(false);\n    });\n\n    it(\"respects loadFromCache: false\", async () => {\n      (localStorage.getItem as jest.Mock).mockReturnValue(\n        JSON.stringify({ ...mockLoginInitResponse, is_cached: true }),\n      );\n      (hankoMock.client.post as jest.Mock).mockResolvedValue({\n        json: () => Promise.resolve(mockLoginInitResponse),\n      });\n      const state = await State.create(hankoMock, flowName, {\n        loadFromCache: false,\n      });\n      expect(hankoMock.client.post).toHaveBeenCalled();\n      expect(state.name).toBe(\"login_init\");\n      expect(state.isCached).toBe(false);\n    });\n  });\n\n  describe(\"static fetchState\", () => {\n    it(\"returns error response on fetch failure\", async () => {\n      (hankoMock.client.post as jest.Mock).mockRejectedValue(\n        new Error(\"Network error\"),\n      );\n      const response = await State.fetchState(\n        hankoMock,\n        \"/continue_with_login_identifier\",\n      );\n      expect(response.name).toBe(\"error\");\n      expect(response.error).toBeDefined();\n    });\n  });\n\n  describe(\"initializeFlowState\", () => {\n    it(\"processes auto-steps if not excluded\", async () => {\n      const nextState = { name: \"preflight\" } as AnyState;\n      (autoSteps.preflight as jest.Mock).mockResolvedValue(nextState);\n      const state = await State.initializeFlowState(\n        hankoMock,\n        flowName,\n        mockPreflightResponse,\n      );\n      expect(autoSteps.preflight).toHaveBeenCalled();\n      expect(state.name).toBe(\"preflight\");\n    });\n  });\n});\n\ndescribe(\"Action\", () => {\n  let hankoMock: jest.Mocked<Hanko>;\n  let state: State<\"login_init\">;\n  const flowName: FlowName = \"login\";\n  const mockResponse: FlowResponse<\"login_init\"> = {\n    name: \"login_init\",\n    csrf_token: \"csrf123\",\n    status: 200,\n    actions: {\n      continue_with_login_identifier: {\n        action: \"continue_with_login_identifier\",\n        href: \"/continue_with_login_identifier\",\n        inputs: {},\n        description: \"\",\n      },\n    },\n    payload: null,\n  };\n  const actionDef = {\n    action: \"continue_with_login_identifier\",\n    href: \"/continue_with_login_identifier\",\n    inputs: { username: { value: \"default\" } },\n    description: \"\",\n  };\n\n  beforeEach(() => {\n    hankoMock = new (jest.requireMock(\n      \"../../../src\",\n    ).Hanko)() as jest.Mocked<Hanko>;\n    state = new State(hankoMock, flowName, mockResponse);\n    localStorage.clear();\n    jest.clearAllMocks();\n  });\n\n  describe(\"constructor\", () => {\n    it(\"initializes action properties\", () => {\n      const action = new Action(actionDef, state);\n      expect(action.name).toBe(\"continue_with_login_identifier\");\n      expect(action.href).toBe(\"/continue_with_login_identifier\");\n      expect(action.enabled).toBe(true);\n      expect(action.inputs).toEqual({ username: { value: \"default\" } });\n    });\n  });\n\n  describe(\"createDisabled\", () => {\n    it(\"creates a disabled action\", () => {\n      const action = Action.createDisabled(\"disabledAction\", state);\n      expect(action.enabled).toBe(false);\n      expect(action.name).toBe(\"disabledAction\");\n      expect(action.href).toBe(\"\");\n    });\n  });\n\n  describe(\"run\", () => {\n    it(\"executes action and returns new state\", async () => {\n      const action = new Action(actionDef, state);\n      const nextResponse: FlowResponse<any> = {\n        ...mockResponse,\n        name: \"nextState\",\n      };\n      (hankoMock.client.post as jest.Mock).mockResolvedValue({\n        json: () => Promise.resolve(nextResponse),\n      });\n      // @ts-ignore\n      const newState = await action.run({ username: \"custom\" });\n      expect(hankoMock.client.post).toHaveBeenCalledWith(\n        \"/continue_with_login_identifier\",\n        {\n          input_data: { username: \"custom\" },\n          csrf_token: \"csrf123\",\n        },\n      );\n      expect(newState.name).toBe(\"nextState\");\n    });\n\n    it(\"throws if action is disabled\", async () => {\n      const action = Action.createDisabled(\"disabledAction\", state);\n      await expect(action.run()).rejects.toThrow(\n        \"Action 'disabledAction' is not enabled\",\n      );\n    });\n\n    it(\"throws if action already invoked\", async () => {\n      const action = new Action(actionDef, state);\n      state.invokedAction = {\n        name: \"previousAction\",\n        relatedStateName: \"login_init\",\n      };\n      await expect(action.run()).rejects.toThrow(\n        \"An action 'previousAction' has already been invoked on state 'login_init'. No further actions can be run.\",\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/setup.ts",
    "content": "const fakeLocalStorage = (function () {\n  return {\n    getItem: jest.fn(),\n    setItem: jest.fn(),\n    removeItem: jest.fn(),\n    clear: jest.fn(),\n  };\n})();\n\nObject.defineProperty(global, \"localStorage\", {\n  value: fakeLocalStorage,\n});\n\nconst fakeCredentials = (function () {\n  return {\n    create: jest.fn(),\n    get: jest.fn(),\n  };\n})();\n\nconst fakeNavigator = (function () {\n  return {\n    credentials: fakeCredentials,\n  };\n})();\n\nObject.defineProperty(global, \"navigator\", {\n  value: fakeNavigator,\n});\n\nexport const fakePublicKeyCredential = (function () {\n  return {\n    isUserVerifyingPlatformAuthenticatorAvailable: jest.fn(),\n    isExternalCTAP2SecurityKeySupported: jest.fn(),\n    isConditionalMediationAvailable: jest.fn(),\n  };\n})();\n\nObject.defineProperty(window, \"PublicKeyCredential\", {\n  value: fakePublicKeyCredential,\n  configurable: true,\n  writable: true,\n});\n\nexport const fakeXMLHttpRequest = (function () {\n  return jest.fn().mockImplementation(() => ({\n    response: \"{}\",\n    open: jest.fn(),\n    setRequestHeader: jest.fn(),\n    getResponseHeader: jest.fn(),\n    send: jest.fn(),\n  }));\n})();\n\nObject.defineProperty(global, \"XMLHttpRequest\", {\n  value: fakeXMLHttpRequest,\n  configurable: true,\n  writable: true,\n});\n\nexport const fakeTimerNow = 1664379699000;\n\njest.useFakeTimers({\n  now: fakeTimerNow,\n});\n\n// eslint-disable-next-line require-jsdoc\nclass MockBroadcastChannel {\n  name: string;\n  message: any;\n\n  constructor(name: string) {\n    this.name = name;\n  }\n\n  postMessage(message: any) {\n    this.message = message;\n  }\n}\n\nObject.defineProperty(global, \"BroadcastChannel\", {\n  value: MockBroadcastChannel,\n  configurable: true,\n  writable: true,\n});\n"
  },
  {
    "path": "frontend/frontend-sdk/tests/types.d.ts",
    "content": "declare global {\n  // eslint-disable-next-line no-unused-vars\n  interface PublicKeyCredential {\n    isExternalCTAP2SecurityKeySupported: () => Promise<boolean>;\n    isConditionalMediationAvailable: () => Promise<boolean>;\n  }\n}\n\nexport {};\n"
  },
  {
    "path": "frontend/frontend-sdk/tsconfig.json",
    "content": "{\n  \"include\": [\n    \"src/**/*\",\n    \"tests/**/*\"\n  ],\n  \"compilerOptions\": {\n    \"paths\": {\n      \"*\": [\n        \"./*\"\n      ]\n    },\n    \"esModuleInterop\": true,\n    \"baseUrl\": \"src\",\n    \"moduleResolution\": \"node\",\n    \"allowSyntheticDefaultImports\": true,\n    \"isolatedModules\": true,\n    \"resolveJsonModule\": true,\n    \"noResolve\": false,\n    \"declaration\": true,\n    \"diagnostics\": true,\n    \"outDir\": \"./dist\",\n    \"lib\":[\"ES2015\", \"ES2016\", \"dom\"],\n    \"sourceMap\": true,\n    \"noImplicitAny\": true,\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"types\": [\"jest\", \"node\", \"@types/jest\"]\n  }\n}\n"
  },
  {
    "path": "frontend/frontend-sdk/tsconfig.prod.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"exclude\": [\n    \"tests/**/*\"\n  ]\n}\n"
  },
  {
    "path": "frontend/package.json",
    "content": "{\n  \"name\": \"hanko-frontend\",\n  \"description\": \"hanko frontend parts\",\n  \"private\": true,\n  \"workspaces\": [\n    \"frontend-sdk\",\n    \"elements\",\n    \"examples/angular\",\n    \"examples/nextjs\",\n    \"examples/react\",\n    \"examples/vue\",\n    \"examples/express\"\n  ],\n  \"scripts\": {\n    \"build\": \"turbo run build\",\n    \"lint\": \"turbo run lint\",\n    \"test\": \"turbo run test\",\n    \"start\": \"turbo run start\",\n    \"build:elements\": \"turbo build --filter=./frontend-sdk --filter=./elements\",\n    \"build:elements:dev\": \"turbo build:dev --filter=./elements\"\n  },\n  \"devDependencies\": {\n    \"turbo\": \"^2.8.18\"\n  },\n  \"packageManager\": \"npm@11.6.2\"\n}\n"
  },
  {
    "path": "frontend/turbo.json",
    "content": "{\n  \"$schema\": \"https://v2-8-7.turborepo.dev/schema.json\",\n  \"tasks\": {\n    \"build\": {\n      \"dependsOn\": [\n        \"^build\"\n      ],\n      \"outputs\": [\n        \"dist/**\",\n        \".next/**\",\n        \"!.next/cache/**\"\n      ]\n    },\n    \"build:dev\": {\n      \"dependsOn\": [\n        \"^build\"\n      ],\n      \"outputs\": [\n        \"dist/**\",\n        \".next/**\",\n        \"!.next/cache/**\"\n      ]\n    },\n    \"start\": {\n      \"dependsOn\": [\n        \"^build\"\n      ],\n      \"outputs\": [\n        \"dist/**\",\n        \".next/**\",\n        \"!.next/cache/**\"\n      ]\n    },\n    \"test\": {\n      \"dependsOn\": [\n        \"build\"\n      ],\n      \"inputs\": [\n        \"src/**/*.tsx\",\n        \"src/**/*.ts\",\n        \"test/**/*.ts\",\n        \"test/**/*.tsx\"\n      ]\n    },\n    \"lint\": {}\n  }\n}\n"
  },
  {
    "path": "quickstart/Dockerfile",
    "content": "# Build the quickstart binary\nFROM --platform=$BUILDPLATFORM golang:1.24 AS builder\n\nARG TARGETARCH\n\nWORKDIR /workspace\nCOPY . .\n\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -a -o quickstart main.go\n\n# Use distroless as minimal base image to package quickstart binary\n# See https://github.com/GoogleContainerTools/distroless for details\nFROM gcr.io/distroless/static:nonroot\nWORKDIR /\nCOPY --from=builder /workspace/quickstart .\nCOPY /public /public\nUSER 65532:65532\n\nENTRYPOINT [\"/quickstart\"]\n"
  },
  {
    "path": "quickstart/README.md",
    "content": "# Quickstart\n\nThis directory contains an example application that showcases\n\n- integration of web component(s) provided through the `@teamhanko/hanko-elements` package (see [elements](../frontend/elements)).\n- server-side validation of JWTs issued by the Hanko [API](../backend) for securing a custom backend/API\n\nThe example is used on https://example.hanko.io/.\n\n## Run the quickstart\n\nThe fastest way to try out Hanko is with [docker-compose](https://www.docker.com/products/docker-desktop/).\n\nClone this repository:\n```\ngit clone https://github.com/teamhanko/hanko.git\n```\n\nThen, in the newly created `hanko` folder, run:\n```\ndocker compose -f deploy/docker-compose/quickstart.yaml -p \"hanko-quickstart\" up --build\n```\n> **Note**: Docker (Desktop) needs to be running in order for the command to run.\n\nAfter the services are up and running, the login page can be viewed at `localhost:8888`. To receive emails without your\nown SMTP server, we added [mailslurper](https://github.com/mailslurper/mailslurper) which will be available at `localhost:8080`.\n\n> **Note**: Hanko services are not published to a registry yet and will be built locally before the services are started.\n"
  },
  {
    "path": "quickstart/go.mod",
    "content": "module github.com/teamhanko/hanko/quickstart\n\ngo 1.24.0\n\nrequire github.com/labstack/echo/v4 v4.9.0\n\nrequire (\n\tgithub.com/golang-jwt/jwt v3.2.2+incompatible // indirect\n\tgithub.com/labstack/gommon v0.3.1 // indirect\n\tgithub.com/mattn/go-colorable v0.1.11 // indirect\n\tgithub.com/mattn/go-isatty v0.0.14 // indirect\n\tgithub.com/stretchr/testify v1.9.0 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/valyala/fasttemplate v1.2.1 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/net v0.47.0 // indirect\n\tgolang.org/x/sys v0.38.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n\tgolang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect\n)\n"
  },
  {
    "path": "quickstart/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=\ngithub.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=\ngithub.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=\ngithub.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=\ngithub.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=\ngithub.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=\ngithub.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=\ngithub.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=\ngithub.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=\ngolang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=\ngolang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=\ngolang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "quickstart/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\tmw \"github.com/labstack/echo/v4/middleware\"\n\t\"github.com/teamhanko/hanko/quickstart/middleware\"\n\t\"html/template\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nfunc main() {\n\tt := &Template{\n\t\ttemplates: template.Must(template.ParseGlob(\"public/html/*.html\")),\n\t}\n\n\thankoUrl := getEnv(\"HANKO_URL\")\n\thankoElementUrl := getEnv(\"HANKO_ELEMENT_URL\")\n\thankoUrlInternal := hankoUrl\n\tif value, ok := os.LookupEnv(\"HANKO_URL_INTERNAL\"); ok {\n\t\thankoUrlInternal = value\n\t}\n\n\t// This is handled as a \"flag\" if set to any value, conditional UI is enabled.\n\t_, conditionalUi := os.LookupEnv(\"HANKO_ENABLE_CONDITIONAL_UI\")\n\n\te := echo.New()\n\te.Renderer = t\n\n\te.Use(mw.LoggerWithConfig(mw.LoggerConfig{\n\t\tFormat: `{\"time\":\"${time_rfc3339_nano}\",\"time_unix\":\"${time_unix}\",\"id\":\"${id}\",\"remote_ip\":\"${remote_ip}\",` +\n\t\t\t`\"host\":\"${host}\",\"method\":\"${method}\",\"uri\":\"${uri}\",\"user_agent\":\"${user_agent}\",` +\n\t\t\t`\"status\":${status},\"error\":\"${error}\",\"latency\":${latency},\"latency_human\":\"${latency_human}\"` +\n\t\t\t`,\"bytes_in\":${bytes_in},\"bytes_out\":${bytes_out},\"referer\":\"${referer}\"}` + \"\\n\",\n\t}))\n\n\te.Use(middleware.CacheControlMiddleware())\n\n\te.Static(\"/static\", \"public/assets\")\n\n\tindexData := IndexData{\n\t\tHankoUrl:        hankoUrl,\n\t\tHankoElementUrl: hankoElementUrl,\n\t\tConditionalUi:   conditionalUi,\n\t}\n\n\te.GET(\"/\", func(c echo.Context) error {\n\t\treturn c.Render(http.StatusOK, \"index.html\", &indexData)\n\t})\n\n\te.GET(\"/unauthorized\", func(c echo.Context) error {\n\t\treturn c.Render(http.StatusOK, \"unauthorized.html\", &indexData)\n\t})\n\n\te.GET(\"/secured\", func(c echo.Context) error {\n\t\treturn c.Render(http.StatusOK, \"secured.html\", &indexData)\n\t}, middleware.SessionMiddleware(hankoUrlInternal))\n\n\te.File(\"/error\", \"public/html/error.html\")\n\n\tif err := e.Start(\":8080\"); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\ntype IndexData struct {\n\tHankoUrl        string\n\tHankoElementUrl string\n\tConditionalUi   bool\n}\n\ntype Template struct {\n\ttemplates *template.Template\n}\n\nfunc (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {\n\treturn t.templates.ExecuteTemplate(w, name, data)\n}\n\nfunc getEnv(key string) string {\n\tif value, ok := os.LookupEnv(key); ok {\n\t\treturn value\n\t}\n\tlog.Fatalf(\"env key not set: %v\", key)\n\treturn \"\"\n}\n"
  },
  {
    "path": "quickstart/middleware/cache_control.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n)\n\nfunc CacheControlMiddleware() echo.MiddlewareFunc {\n\treturn func(next echo.HandlerFunc) echo.HandlerFunc {\n\t\treturn func(c echo.Context) error {\n\n\t\t\tc.Response().Header().Set(echo.HeaderCacheControl, \"no-store\")\n\n\t\t\treturn next(c)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "quickstart/middleware/session.go",
    "content": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/labstack/echo/v4\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc SessionMiddleware(hankoUrl string) echo.MiddlewareFunc {\n\tclient := http.Client{}\n\treturn func(next echo.HandlerFunc) echo.HandlerFunc {\n\t\treturn func(c echo.Context) error {\n\t\t\tcookie, err := c.Cookie(\"hanko\")\n\t\t\tif err == http.ErrNoCookie {\n\t\t\t\treturn c.Redirect(http.StatusTemporaryRedirect, \"/unauthorized\")\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\trequestBody := CheckSessionRequest{SessionToken: cookie.Value}\n\n\t\t\tbodyJson, err := json.Marshal(requestBody)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to marshal request body: %w\", err)\n\t\t\t}\n\t\t\thttpReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf(\"%s/sessions/validate\", hankoUrl), bytes.NewReader(bodyJson))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\t\tresponse, err := client.Do(httpReq)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer response.Body.Close()\n\n\t\t\tif response.StatusCode != http.StatusOK {\n\t\t\t\treturn fmt.Errorf(\"failed to get session response: %d\", response.StatusCode)\n\t\t\t}\n\n\t\t\tresponseBytes, err := io.ReadAll(response.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tvar sessionResponse CheckSessionResponse\n\t\t\terr = json.Unmarshal(responseBytes, &sessionResponse)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif !sessionResponse.IsValid {\n\t\t\t\treturn c.Redirect(http.StatusTemporaryRedirect, \"/unauthorized\")\n\t\t\t}\n\t\t\tlog.Printf(\"session for user '%s' verified successfully\", sessionResponse.UserID)\n\t\t\tc.Set(\"token\", cookie.Value)\n\t\t\tc.Set(\"user\", sessionResponse.UserID)\n\n\t\t\treturn next(c)\n\t\t}\n\t}\n}\n\ntype CheckSessionRequest struct {\n\tSessionToken string `json:\"session_token\"`\n}\n\ntype CheckSessionResponse struct {\n\tIsValid        bool   `json:\"is_valid\"`\n\tExpirationTime string `json:\"expiration_time\"`\n\tUserID         string `json:\"user_id\"`\n}\n"
  },
  {
    "path": "quickstart/public/assets/css/common.css",
    "content": "@import url(\"fonts.css\");\n\nbody {\n    font-size: 16px;\n    font-family: Inter, sans-serif;\n    color: white;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n    background-color: #05304D;\n}\n\n.nav__itemList {\n    list-style-type: none;\n    margin: 0;\n    padding: 0;\n    overflow: hidden;\n}\n\n.nav__listItem {\n    float: right;\n}\n\n.nav__link,\n.nav__link:visited,\n.nav__link:hover,\n.nav__link:active {\n    display: block;\n    margin: 2.5rem;\n    color: white;\n    background-color: transparent;\n    text-decoration: none;\n}\n\n.nav__link:hover {\n    text-decoration: underline;\n}\n\n.content {\n    margin: auto;\n    margin-bottom: 30px;\n    padding: 0 100px;\n    max-width: 1200px;\n}\n\n@media screen and (max-width: 700px) {\n    .content {\n        padding: 0;\n    }\n}\n\n.footer {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    flex-direction: column;\n    margin-top: 100px;\n}\n\n.footer img {\n    padding-bottom: 2rem;\n}\n\nhanko-auth, hanko-profile {\n    /* Color Scheme */\n    --color: white;\n    --color-shade-1: #A6B6C0;\n    --color-shade-2: #355970;\n\n    --brand-color: #B3CDFF;\n    --brand-color-shade-1: #8EADDA;\n    --brand-contrast-color: #011726;\n\n    --background-color: #05304D;\n    --error-color: #FF6068;\n    --link-color: #B3CDFF;\n\n    /* Font Styles */\n    --font-weight: 400;\n    --font-size: 16px;\n    --font-family: \"Inter\", sans-serif;\n\n    /* Border Styles */\n    --border-radius: 5px;\n    --border-style: solid;\n    --border-width: 1px;\n\n    /* Item Styles */\n    --item-height: 40px;\n    --item-margin: .75em 0;\n\n    /* Input Styles */\n    --input-min-width: 16em;\n\n    /* Button Styles */\n    --button-min-width: 6em;\n\n    /* Container Styles */\n    --container-padding: 0;\n    --container-max-width: 800px;\n\n    /* Headline Styles */\n    --headline1-font-size: 24px;\n    --headline1-margin: 0 0 1rem;\n\n    --headline2-font-size: 18px;\n    --headline2-margin: 1rem 0 .5rem;\n}\n"
  },
  {
    "path": "quickstart/public/assets/css/fonts.css",
    "content": "/* inter-regular - latin */\n@font-face {\n    font-family: 'Inter';\n    font-style: normal;\n    font-weight: 400;\n    src: url('../fonts/inter-v12-latin-regular.eot'); /* IE9 Compat Modes */\n    src: local(''),\n    url('../fonts/inter-v12-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */\n    url('../fonts/inter-v12-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */\n    url('../fonts/inter-v12-latin-regular.woff') format('woff'), /* Modern Browsers */\n    url('../fonts/inter-v12-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */\n    url('../fonts/inter-v12-latin-regular.svg#Inter') format('svg'); /* Legacy iOS */\n}\n/* inter-500 - latin */\n@font-face {\n    font-family: 'Inter';\n    font-style: normal;\n    font-weight: 500;\n    src: url('../fonts/inter-v12-latin-500.eot'); /* IE9 Compat Modes */\n    src: local(''),\n    url('../fonts/inter-v12-latin-500.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */\n    url('../fonts/inter-v12-latin-500.woff2') format('woff2'), /* Super Modern Browsers */\n    url('../fonts/inter-v12-latin-500.woff') format('woff'), /* Modern Browsers */\n    url('../fonts/inter-v12-latin-500.ttf') format('truetype'), /* Safari, Android, iOS */\n    url('../fonts/inter-v12-latin-500.svg#Inter') format('svg'); /* Legacy iOS */\n}\n/* inter-600 - latin */\n@font-face {\n    font-family: 'Inter';\n    font-style: normal;\n    font-weight: 600;\n    src: url('../fonts/inter-v12-latin-600.eot'); /* IE9 Compat Modes */\n    src: local(''),\n    url('../fonts/inter-v12-latin-600.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */\n    url('../fonts/inter-v12-latin-600.woff2') format('woff2'), /* Super Modern Browsers */\n    url('../fonts/inter-v12-latin-600.woff') format('woff'), /* Modern Browsers */\n    url('../fonts/inter-v12-latin-600.ttf') format('truetype'), /* Safari, Android, iOS */\n    url('../fonts/inter-v12-latin-600.svg#Inter') format('svg'); /* Legacy iOS */\n}\n"
  },
  {
    "path": "quickstart/public/assets/css/index.css",
    "content": "@import url(\"common.css\");\n\n.main {\n    margin-top: 10%;\n}\n\n.auth-container {\n    max-width: 360px;\n    min-width: 200px;\n    margin: auto;\n    border-radius: 16px;\n    padding: 25px;\n}\n\n@media screen and (max-width: 470px) {\n    .auth-container {\n        padding: 10px;\n    }\n}\n\n"
  },
  {
    "path": "quickstart/public/assets/css/secured.css",
    "content": "@import url(\"common.css\");\n\n.profile {\n    margin-bottom: 20px;\n}\n\n.profile-container {\n    max-width: 700px;\n    min-width: 200px;\n    margin: auto;\n    border-radius: 16px;\n    padding: 25px;\n}\n\nhanko-profile {\n    --headline1-margin: 2em 0 1em;\n    --input-min-width: 20em;\n}\n\n@media screen and (max-width: 420px) {\n    hanko-profile {\n        --input-min-width: 14em;\n    }\n}\n"
  },
  {
    "path": "quickstart/public/html/error.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <title>Hanko Web Component Example</title>\n    <link rel=\"stylesheet\" href=\"static/css/common.css\"/>\n    <link href=\"../static/img/Favicon_32x32.png\" rel=\"shortcut icon\" type=\"image/x-icon\">\n    <link href=\"../static/img/Favicon_256x256.png\" rel=\"apple-touch-icon\">\n</head>\n<body>\n\n<header>\n    <nav class=\"nav\">\n        <ul class=\"nav__itemList\">\n            <li class=\"nav__listItem\">\n                <a class=\"nav__link\" href=\"/\">Back to login</a>\n            </li>\n        </ul>\n    </nav>\n</header>\n\n<main class=\"main\">\n    <div class=\"content\">\n        <h1>Error</h1>\n        <p>Something went wrong</p>\n    </div>\n</main>\n\n</body>\n</html>\n"
  },
  {
    "path": "quickstart/public/html/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"theme-color\" content=\"#042F4B\">\n    <title>Hanko Web Component Example</title>\n    <link rel=\"stylesheet\" href=\"static/css/index.css\">\n    <link href=\"../static/img/Favicon_32x32.png\" rel=\"shortcut icon\" type=\"image/x-icon\">\n    <link href=\"../static/img/Favicon_256x256.png\" rel=\"apple-touch-icon\">\n</head>\n<body>\n\n<main class=\"main\">\n    <div class=\"auth-container\">\n        <hanko-auth lang=\"en\" {{if .ConditionalUi}}experimental=\"conditionalMediation\"{{end}}></hanko-auth>\n    </div>\n</main>\n\n<footer class=\"footer\">\n    <img alt=\"Powered by Hanko\" src=\"static/img/poweredBy.svg\"/>\n</footer>\n\n<script type=\"module\">\n    import { register } from \"{{.HankoElementUrl}}\";\n\n    const { hanko } = await register(\"{{.HankoUrl}}\", { sessionCheckInterval: 5000});\n\n    hanko.onSessionCreated(() => window.location.href = \"/secured\");\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "quickstart/public/html/secured.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n    <meta name=\"theme-color\" content=\"#042F4B\">\n    <title>Hanko Web Component Example</title>\n    <link rel=\"stylesheet\" href=\"static/css/secured.css\"/>\n    <link href=\"../static/img/Favicon_32x32.png\" rel=\"shortcut icon\" type=\"image/x-icon\">\n    <link href=\"../static/img/Favicon_256x256.png\" rel=\"apple-touch-icon\">\n</head>\n<body>\n<header>\n    <nav class=\"nav\">\n        <ul class=\"nav__itemList\">\n            <li class=\"nav__listItem\">\n                <a href=\"#\" class=\"nav__link\" id=\"logout-link\">Logout</a>\n            </li>\n        </ul>\n    </nav>\n</header>\n\n<main class=\"main\">\n    <div class=\"content\">\n        <div class=\"profile\">\n            <div class=\"profile-container\">\n                <div>\n                    <h1>My Profile</h1>\n                </div>\n                <hanko-profile lang=\"en\"></hanko-profile>\n            </div>\n        </div>\n    </div>\n</main>\n\n<footer class=\"footer\">\n    <img alt=\"Powered by Hanko\" src=\"static/img/poweredBy.svg\"/>\n</footer>\n\n<script type=\"module\">\n    import { register } from \"{{.HankoElementUrl}}\";\n\n    const { hanko } = await register(\"{{.HankoUrl}}\", { sessionCheckInterval: 5000 });\n\n    const session = await hanko.validateSession();\n\n    if (!session.is_valid) {\n        window.location.href = \"/unauthorized\";\n    }\n\n    hanko.onUserDeleted(() => window.location.href = \"/\" );\n    hanko.onSessionExpired(() => window.location.href = \"/unauthorized\");\n    hanko.onUserLoggedOut(() => window.location.href = \"/\");\n\n    document.getElementById(\"logout-link\")\n        .addEventListener(\"click\", (event) => {\n            event.preventDefault();\n            hanko.logout().catch(console.error);\n        });\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "quickstart/public/html/unauthorized.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <title>Hanko Web Component Example</title>\n    <link rel=\"stylesheet\" href=\"static/css/common.css\"/>\n    <link href=\"../static/img/Favicon_32x32.png\" rel=\"shortcut icon\" type=\"image/x-icon\">\n    <link href=\"../static/img/Favicon_256x256.png\" rel=\"apple-touch-icon\">\n</head>\n<body>\n\n<header>\n    <nav class=\"nav\">\n        <ul class=\"nav__itemList\">\n            <li class=\"nav__listItem\">\n                <a class=\"nav__link\" href=\"/\">Back to login</a>\n            </li>\n        </ul>\n    </nav>\n</header>\n\n<main class=\"main\">\n    <div class=\"content\">\n        <h1>Unauthorized</h1>\n    </div>\n</main>\n\n<script type=\"module\">\n    import { register } from \"{{.HankoElementUrl}}\";\n\n    const { hanko } = await register(\"{{.HankoUrl}}\", { sessionCheckInterval: 5000});\n\n    hanko.onSessionCreated(() => window.location.href = \"/secured\");\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "skaffold.yaml",
    "content": "apiVersion: skaffold/v2beta29\nkind: Config\nmetadata:\n  name: hanko-tenant\nbuild:\n  artifacts:\n  - image: ghcr.io/teamhanko/hanko\n    context: backend\n    docker:\n      dockerfile: Dockerfile\n  - image: ghcr.io/teamhanko/hanko/quickstart\n    context: quickstart\n    docker:\n      dockerfile: Dockerfile\n  - image: ghcr.io/teamhanko/hanko/elements\n    context: frontend\n    docker:\n      dockerfile: Dockerfile\ndeploy:\n  kustomize:\n    paths:\n    - deploy/k8s/overlays/quickstart\nprofiles:\n- name: quickstart\n  deploy:\n    kustomize:\n      paths:\n      - deploy/k8s/overlays/quickstart\n- name: thirdparty-x-domain\n  deploy:\n    kustomize:\n      paths:\n        - deploy/k8s/overlays/thirdparty-x-domain\n"
  }
]